【问题标题】:Why do properties behave this way?为什么属性会这样表现?
【发布时间】:2010-04-12 01:34:32
【问题描述】:

来自http://csharpindepth.com/Articles/Chapter8/PropertiesMatter.aspx

using System;

struct MutableStruct
{
    public int Value { get; set; }

    public void SetValue(int newValue)
    {
        Value = newValue;
    }
}

class MutableStructHolder
{
    public MutableStruct Field;
    public MutableStruct Property { get; set; }
}

class Test
{    
    static void Main(string[] args)
    {
        MutableStructHolder holder = new MutableStructHolder();
        // Affects the value of holder.Field
        holder.Field.SetValue(10);
        // Retrieves holder.Property as a copy and changes the copy
        holder.Property.SetValue(10);

        Console.WriteLine(holder.Field.Value);
        Console.WriteLine(holder.Property.Value);
    }
} 

1) 为什么要制作(财产的?)副本?
2) 将代码更改为holder.Field.Valueholder.Property.Value = 10 时,出现以下错误。这让我大吃一惊。

不能修改'MutableStructHolder.Property'的返回值,因为它不是变量

为什么不允许我在属性内赋值!?!两个属性都是get/set!

最后,为什么你会想要 1 和 2 中提到的行为? (它从来没有出现在我身上,我总是使用 get only 属性)。

请解释清楚,我无法想象第二个比第一个少得多。这对我来说太奇怪了。

【问题讨论】:

  • 现在您知道为什么可变结构是 C# 中最糟糕的做法了。按值复制和变异的组合并不是一个好的组合。

标签: c# .net properties


【解决方案1】:

1) 正在制作 Property 的副本,因为它是 struct,而 struct 是值类型(与 class 不同)并且是按值复制的,而不是按引用复制。

2) 您看到的“无法修改返回值”错误源于结构的“按值复制”性质。如果您修改了结构(在MutableStruct 上设置Value 通过PropertyMutableStructHolder 上),您的更改将不会反映在它上,因为您将修改结构的 copy ,而不是属性中的结构。由于您永远不会想要这种行为,因此 C# 编译器会阻止您这样做。

现在,如果MutableStructclass,那么通过属性对其进行修改绝对没有问题(C# 编译器允许您这样做),因为您将使用实际实例而不是一份。

【讨论】:

    【解决方案2】:

    要回答 #1:考虑一下没有自动生成属性的情况。你会有Property { get { return xxx; } } 那就是复制的地方。看起来引用属性实际上是调用 getter 方法。如果您有以下情况,您会期望就地编辑:

    private MutableStruct v;
    public MutableStruct f()
    {
      return v;
    }
    
    f() = new MutableStruct();
    

    当然不是,我们都依赖 return 来制作结构的副本,以便保护私有变量免受外部修改。所以这就是它以这种方式工作的原因。

    现在,如果您认为应该使用结果值自动调用属性设置器,请让我指出,非常困难在编译时无法知道是否调用对结构的方法之一进行更改(考虑结构是否在不同的程序集中定义)。并且将值写回不仅效率低下而且无害,它可能会破坏正确性。考虑一个多线程场景,其中仅从受 ReaderWriterLock 保护的属性读取的线程突然回写给它。各种痛苦和痛苦都会发生。因此,C# 编译器不会自动将属性设置为计算结果。

    编辑:您想知道为什么在结构上设置字段会生成错误消息,但调用方法来做同样的事情却不会。好吧,该方法可能具有有用的副作用,因此您仍然需要能够在结构的临时副本上调用它。考虑以下变化:

    struct Mutable
    {
        public static int Sum = 0;
    
        public int x;
        public Mutable(int x) { this.x = x; }
        public void Total() { Sum += x; }
    }
    
    class Container
    {
        public Mutable Field;
        public Mutable Property { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Container c = new Container();
            c.Field = new Mutable(1);
            c.Property = new Mutable(2);
            c.Field.Total();
            c.Property.Total();
            Console.Out.WriteLine(Mutable.Sum);
        }
    }
    

    【讨论】:

    • 我完全忘记了它会调用 'get' 来访问 value 属性。
    • @acidzombie:我根本无法理解这个答案。正如其他答案正确指出的那样,#1 的实际答案是 MutableStruct 是一个值类型(结构)。
    • @BlueRaja:其他解释的结构是按值传递的。这个答案是说为什么要传递结构(使用get)并且我没有像我认为的那样直接访问Value(仅设置,没有get)
    • @BlueRaja:问题是为什么当属性具有可访问的设置器时没有更新。其他答案都没有尝试解决为什么编译器不能只使用 setter 将更新的值存储回属性。
    • 您无法重现错误消息的原因(我看到您在编辑历史记录中提到过)是因为我使用的是 VS2010(rc1)。您不需要代码示例,我只需要在设置之前提醒我有一个获取! ://
    【解决方案3】:

    问题 1:您应该阅读结构和类,因为它们的工作原理解释了您所描述的所有“症状”。结构不是通过引用传递的,而是实际上通过复制被传递的对象 - copies 是关键字。因此,当属性返回一个结构时,它实际上是一个副本,而不是您要更改其值的“真实”对象。

    至于问题 2: C# 编译器意识到直接在通过属性检索的 struct-object 上设置变量是没有意义的,因此会向您提供错误提示。通过属性检索到的对象是否具有 setter 不会改变这种行为,无论哪种方式,您都会收到错误。

    我建议您阅读 Lasse Karlsen 对“The difference between structs and classes”问题的回答,而不是写关于类与结构的文章

    【讨论】:

      【解决方案4】:

      结构是按值传递的,所以你所拥有的只是副本。另外:

      public int Value { get; set; }
      

      的简写
      private int _Value;
      public int Value { 
         get { return _Value; }  
         set { _Value = value; }
      }
      

      如您所见,有很多抄袭行为,因此投诉是意料之中的。如果你改用class,这是一个引用类型,你就不会有这个问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-15
        • 1970-01-01
        • 2018-10-15
        • 1970-01-01
        • 2018-07-06
        • 2021-09-17
        相关资源
        最近更新 更多