【问题标题】:Difference between modifying a variable and a property修改变量和属性的区别
【发布时间】:2010-09-20 07:15:31
【问题描述】:

对此可能有一个非常合乎逻辑的解释,但我有一个问题。

假设我有一个名为 _rect 的 Rectangle 类型变量。我现在可以说 _rect.X = 50;没有任何问题。

现在我有一个类,它有一个名为 Rect 的属性,它公开了内部变量 _rect。

然后,如果我尝试写 Rect.X = 50; 我得到以下编译错误:

不能修改'TestClass.Rect'的返回值,因为它不是一个变量。

我可以为不可变类型写Rect = new Rectangle( 50, Rect.Y, Rect.Width, Rect.Height),但对于非不可变类型,还有其他方法吗?

我想为这个矩形字段使用自动属性,但是不能在类本身中修改它真的很烦人。

有没有什么办法可以不创建一个支持字段并删除自动属性?

【问题讨论】:

  • 您能向我们展示您的 Rect 类代码吗?为什么不使用内置的 Rect 类?
  • 矩形是类还是结构?如果它是一个结构,请尝试使其成为一个类
  • 给我们一个完整的代码清单,我很难想象你在说什么,这将有助于澄清你的问题。
  • 抱歉给您带来了困惑。 Rect 是一个标准的 System.Drawing.Rectangle。我已更新我的问题以消除混淆。
  • 另请参阅此问题(非重复):stackoverflow.com/questions/3618693/…

标签: c# .net


【解决方案1】:

此错误的原因是因为Rectangle 是值类型(结构),而不是引用类型(类)。您不能修改X 属性,因为当您使用Rect 属性getter 时,会返回矩形的新值(getter 是一个函数)。如果它是一个引用类型,你正在操纵指针,这是可能的。

这是需要注意的值与引用类型的一个重要方面。

【讨论】:

  • 可以肯定的是,没有技术原因导致编译器无法完成这项工作。编译器会为用户生成各种额外的代码(例如装箱),为什么不在这里呢? (事实上​​,原因可能是它被认为不重要,因为结构无论如何都应该是不可变的。)
  • @Konrad - 结构在作为结果传递时被复制,因此该操作根本没有任何意义。这不是因为不变性(结构是可变的)。但是,如果您检索对象的副本并更改该副本中的字段 - 这只是一个 NOOP。
  • @viraptor:我不同意。该操作显然是有道理的:改变一个属性。编译器可以通过生成等效于var tmp = foo.Property; tmp.x = newX; foo.Property = tmp; 的代码来轻松完成这项工作——编译器实际上这样做这适用于等效情况:当您有一个值类型属性并编写foo.Property += 1工作(在 Mono 编译器上测试)。编译器清楚地将代码改写为上述内容。
  • @Konrad:这些情况是不等价的——如果Property 的类型是一个结构,那么对于foo.Property.x += 1,你会得到tmp=foo.Property; tmp.x+=1 的等价物(其中tmp 变成了一个属性的副本)。如果属性本身是一个简单的值类型,那么您不需要修改值本身的任何内容。您生成一个新值并分配给属性本身,而不是它的一部分。属性 getter 保证您获得类型的 副本。例如,您将获得1副本 并分配回2。如果您获得foo.x副本 并对其进行修改,则分配给x副本
  • @viraptor:我同意,我自己也注意到了这种差异——另见下面的 cmets stackoverflow.com/questions/441309/why-are-mutable-structs-evil/…
【解决方案2】:

访问属性实际上是一个返回值类型副本的函数调用。矩形 X = 50;只会修改这个临时副本,而不是支持字段本身。

当属性不自动实现时,可以创建一个额外的属性 RectX,用于获取和设置矩形的 X 属性。

【讨论】:

  • 我现在觉得很愚蠢,因为不明白为什么 new Rectangle(...) 有效,而不是 Rect.X = 50,因为两者都可以在副本上工作,但是当我写 new Rectangle我正在调用集合而不是获取。感谢您对此表示清楚。所以这基本上意味着没有。如果我想要这种行为,我必须使用支持字段。
  • 当使用支持字段时,行为仍然是相同的,getter 和 setter 仍然是方法,所以您仍然会获得 Rectangle 实例的值副本。您想要的行为仅适用于引用类型,因为这样您就获得了对原始对象实例的“引用”。然而 rectangle 是一个值类型。
【解决方案3】:

【讨论】:

    【解决方案4】:
      class RectangleWithoutFields 
      {
         // just autoproperties, no fields
    
         public int X { get; set; }
    
         public int Y { get; set;}
    
    
         public RectangleWithoutFields()
         {
             X = 0;
    
             Y = 0; 
         }
    
         public void ChangeProperties(int x, int y)
    
         {
             X = x;
    
             Y = y;
         }
     }
    

    【讨论】:

    • 我不是想在这里创建一个新矩形,而是让它与标准矩形一起工作:)
    猜你喜欢
    • 2011-06-28
    • 2020-11-03
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 2016-02-23
    • 2023-04-06
    • 2014-08-22
    • 2010-10-23
    相关资源
    最近更新 更多