为什么属性会这样?

时间:2010-04-12 01:34:32

标签: c# .net properties

来自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中提到的行为? (它从来没有出现过,我总是只使用获得属性)。

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

4 个答案:

答案 0 :(得分:3)

1)正在制作Property的副本,因为它是structstruct是值类型(与class不同)并且由价值,而不是参考。

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

现在,如果MutableStructHolderMutableStruct,那么通过属性修改它绝对没有问题(并且C#编译器允许你这样做),因为你将使用实际的实例,而不是副本。

答案 1 :(得分:2)

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

关于问题2: C#编译器意识到直接在通过属性检索的struct-object上设置变量是没有意义的,因此会向您提供错误告诉您。通过属性检索的对象是否具有setter不会改变此行为,您将以任何方式接收错误。

我建议您阅读Lasse Karlsen对“The difference between structs and classes”问题的回答

,而不是对类和结构进行写作。

答案 2 :(得分:2)

回答#1:考虑没有自动生成属性的情况。你有Property { get { return xxx; } }这就是复制的地方。看起来像是指一个属性,实际上是在调用getter方法。如果您有以下内容,您是否期望进行就地编辑:

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

f() = new MutableStruct();

当然不是,我们都依赖于返回来制作结构的副本,以便保护私有变量免受外部修改。这就是为什么它按照它的方式工作的原因。

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

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

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);
    }
}

答案 3 :(得分:0)

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

public int Value { get; set; }

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

正如您所看到的,正在进行大量复制,因此需要投诉。如果您改为使用class作为参考类型,则不会出现此问题。