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?
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...
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.
Have TypeB
implement an interface that defines X
with only a getter. Expose a property with this interface type on TypeA
. Now you can only modify the property's properties when you do a downcast, which is the most you can aim for.
This method will give you compile time safety. If you only need runtime safety, look at CodeNaked's answer.
Your current code will not allow what you want. You cannot make int X read / write and only make it read only when B is stored in A. If you really want to keep the int X immutable, than don't keep it in type B. Instead keep the int value in A instead, and make it a read only value there.
What if you made the property B in class A a readonly field? That way it could only set a construction and not changed afterwards.
Also, what about an internal/private setter? Does that fill the needs?
精彩评论