0

我经常遇到我希望我的类属性表现得像结构一样的情况。(编辑:实际上,我误解了结构的工作原理。我不希望我的属性表现得完全像一个结构;我希望它在被操作和复制时表现不同。)

我想知道这是否可能,如果可以,这是否是一个好主意。

假设我有 2 个简单的类:

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 {...}
    ...
}

现在我想像这样访问 Cube.Center:

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. 

据我了解,如果 Point where 是一个结构而不是一个类,它将按值传递,上面将是预期的行为。(编辑:我误解了结构的工作原理。类似的东西cube.Center.X += 10.0不适用于结构)

然而:

  • 可变结构是邪恶的
  • 对象可能包含比这个小示例更多的字段,Microsoft 建议仅使用非常小的内存占用的结构
  • 最重要的是:一个结构不能从一个类继承,所以这样的事情是不可能的结构:有一个私有的子类 Point 为立方体中心,每当它改变时都会做一些额外的事情。

    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.
    

所以我正在寻求的行为是这样的: - 应该可以修改子属性,如cube1.Center.X. - 每当发生这种情况时,我希望我的自定义cubeCenter设置器执行。- 但是,当有人抓住整个 Center:Point p = cube1.Center时,分配的对象应该只是一个Point没有连接到立方体的普通对象

这可能吗,也许使用隐式转换运算符?

这是一个糟糕的设计选择吗?我愿意接受其他建议。

(我考虑的一件事是使 Points 不可变以避免所有这些问题。但是在这种情况下,我找不到用于更改中心的不难看的语法:)

    // this is the immutable equivalent to cube1.Center.X += 10.0
    cube1.Center = cube1.Center.WithX ( cube1.Center.X + 10.0 )
4

4 回答 4

2

我倾向于一个不可变的struct Point. 移动它很简单:

public static Point operator +(Point a, Point b) 
{
   return new Point(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
}
⋮
cube1.Center = cube1.Center + new Point(10, 0, 0);

其他一切都应该很好地到位。

于 2012-08-12T08:28:24.563 回答
1

您可以编写一个克隆方法并使副本显式,这很好。为了便于阅读,您必须明确区分是否复制,否则调试代码可能是一场噩梦。

public class Point
{
    public virtual Double X {get {return this.x;} set{this.x = value;}}
    public virtual Double Y {...}
    public virtual Double Z {...}

    public Point Clone() {
       return this.MemberwiseClone();
    }
}


Point point = cube.Center.Clone();
point.X += 10.0 // cube1 will NOT move here

MemberwiseClone将制作对象的浅拷贝,您必须自己克隆嵌套的引用类型。

public class Cube
{
   public Point Center {get ; set;}

   public Cube Clone() {
       var cube = this.MemberwiseClone();
       cube.Center = this.Center.Clone();
       return cube;
   }

}

于 2012-08-12T08:45:12.503 回答
0

要创建真正的值语义,只需保留一个Point永远不会传递且永远不会替换它的实例(您需要将克隆功能添加到Point):

public class Cube {
    private readonly Point center = new Point();

    public Point Center {
        get {
            return center.Clone();
        }
        set {
            center.X = value.X;
            center.Y = value.Y;
        }
    }
    ... // some more cube properties
}

请注意,这些语义会阻止您使用cube.Center.X = ...代码 - 但对于结构也是如此。

于 2012-08-12T08:26:06.437 回答
0

不可能准确地得到你想要的,因为属性的 getter 无法知道该值是用于操作还是用于复制。

您可以封装该点,并提供克隆它的方法和操作它的属性:

public class Cube {
  private Point _center;
  public Point CopyCenter() { return _center.Clone(); }
  public double CenterX { get { return _center.X; } set { _center.X = value; } }
  public double CenterY { get { return _center.Y; } set { _center.Y = value; } }
  public double CenterZ { get { return _center.Z; } set { _center.Z = value; } }
}

用法的语法不会完全相同,但至少很清楚发生了什么。

于 2012-08-12T08:49:38.950 回答