I keep running into cases where I want my class properties to behave as if they were structs. (edit: actually, I misunderstood how structs work. I do not want my property to behave exactly like a struct; I want it to behave differently when it is manipulated vs. copied.)
I am wondering if this is possible, and if yes, whether it would be a good idea.
Suppose I have 2 simple classes:
public class Cube
{
public Point Center {get ; set;}
... // some more cube properties
}
public class Point
{
public virtual Double X {get {return this.x;} set{this.x = value;}}
public virtual Double Y {...}
public virtual Double Z {...}
...
}
Now I want to access Cube.Center like this:
Cube cube = new Cube();
cube.Center.X += 10.0; // cube1 should move 10.0 in X direction
Point point = cube.Center;
point.X += 10.0 // cube1 should NOT move here
Cube cube2 = new Cube();
cube2.Center = point;
cube2.Center.Y += 5.0; // cube2 should move, but cube1 should stay in place.
As far as I understand, if Point where a struct instead of a class, it would be passed by value and the above would be the expected behavior. (edit: I misunderstood how structs work. Something like cube.Center.X += 10.0
is not posible with a struct)
However:
- mutable structs are evil
- the objects may contain more fields than this small example, Microsoft recommends structs only with a very small memory footprint
most importantly: a struct cannot inherit from a class, so things like this are not possible with a struct: Having a private child class of Point for the cube center, that does some extra stuff whenever it is changed.
public class Cube { // private child of Point, can only be used by Cube private class cubeCenter: Point { private Cube cube; // The cube this point belongs to public override double X { get {return base.X} // custom setter: whenever X changes, // recalculate the bounding box of the cube set {base.X=value; cube.recalculateBoundingBox();} } ... // + other overrides & constructor } private cubeCenter _center; public Point Center { get {return this._center} set {this._center = new cubeCenter(value, this);} } ... } cube1.Center.X += 10.0; // now cube1 moves AND its bounding box is recalculated.
So the behavior I am seeking is this:
- It should be possible to modify sub-properties like cube1.Center.X
.
- Whenever this happens, I want mys custom cubeCenter
setter to execute.
- However, when someone grabs the whole Center: Point p = cube1.Center
, the assigned object should just be a normal Point
without connection to cube
Is this possible, perhaps with an implicit conversion operator?
Is this a bad design choice? I am open for other suggestions.
(One thing I considered is making Points immutable to avoid all these problems. However I cannot find a non-ugly syntax for changing the center in this case:)
// this is the immutable equivalent to cube1.Center.X += 10.0
cube1.Center = cube1.Center.WithX ( cube1.Center.X + 10.0 )