Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disallow setting property's property

Tags:

c#

.net

I’m not sure what this is called, or where I did see this, so I don’t really know what to search for. Therefore I’m trying to explain what I mean.

Suppose I have the following class structure, where TypeB is used within TypeA:

class TypeA
{
    public TypeB B
    { get; set }
}

class TypeB
{
    public int X
    { get; set }
}

Now when I have an instance of TypeA, I want to disallow, that I can directly modify the values of TypeB without reassigning. So basically I want to prevent this:

TypeA a = new TypeA();

// this should not be possible:
a.B.X = 1234;

// but this should work:
TypeB b = a.B;
b.X = 1234;
a.B = b;

The reason is that I am persisting the object in a special way, so to keep track of changes in TypeB correctly, I want to require that the value of B within TypeA is reassigned.

I think that I have seen a similar thing before with some built-in object, where this threw an error and compile time. What is this called, and how can I achieve it in my custom types?

like image 692
poke Avatar asked Jun 13 '11 22:06

poke


2 Answers

You can return a copy of the TypeB object from the property getter in TypeA:

class TypeA
{
    private TypeB _b;
    public TypeB B
    {
        get { return (TypeB)_b.Clone(); }
        set { _b = value; }
    }
}

This will prevent modifying the properties of _b directly. However, it won't actually disallow to do a.B.X = 1234;, but the instruction will have no effect (it will only modify a copy that will immediately be discarded).

Unless TypeB is a struct, there is no way to prevent such an instruction:

  • it can't be detected at compile time, because the compiler doesn't know that it would modify a copy (again, unless it's a struct)
  • it can't be detected at runtime either, because you can't tell the difference between:

    TypeB b = a.B;
    b.X = 1234;
    

    and

    a.B.X = 1234;
    

    In both cases the property is called the same way, and there is no way to know what the calling code is doing with the result...

like image 164
Thomas Levesque Avatar answered Sep 30 '22 16:09

Thomas Levesque


In WPF/Silverlight there are two concepts of having "sealed" and "frozen" objects. Basically, once sealed/frozen, the object cannot be changed. You may be able to apply the same logic here:

public class SealableObject {

    public bool IsSealed { get; private set; }

    public void Seal() {
        if (this.IsSealed)
            throw new InvalidOperationException("The object is already sealed");
        this.IsSealed = true;
    }

    protected void VerifyNotSealed() {
        if (this.IsSealed)
            throw new InvalidOperationException("Object is sealed");
    }
}

Then you would need to check IsSealed in your derived class TypeB, like so:

class TypeB : SealableObject
{
    private int x;
    public int X
    {
        get { return this.x; }
        set {
            this.VerifyNotSealed();
            this.x = value;
        }
}

And then you'd need to seal TypeB when it is assigned to your TypeA property.

like image 44
CodeNaked Avatar answered Sep 30 '22 16:09

CodeNaked