[英]Why do mutations on readonly structs not break?
在 C# 中,如果你有一个像这样的struct
:
struct Counter
{
private int _count;
public int Value
{
get { return _count; }
}
public int Increment()
{
return ++_count;
}
}
你有一个这样的程序:
static readonly Counter counter = new Counter();
static void Main()
{
// print the new value from the increment function
Console.WriteLine(counter.Increment());
// print off the value stored in the item
Console.WriteLine(counter.Value);
}
程序的输出将是:
1
0
这似乎完全错误。 我要么期望输出是两个 1(如果Counter
是一个class
或者如果struct Counter : ICounter
和counter
是一个ICounter
)或者是一个编译错误。 我意识到在编译时检测这一点是一件相当困难的事情,但这种行为似乎违反了逻辑。
这种行为是否有超出实现难度的原因?
structs
是值类型,因此具有值类型语义。 这意味着每次访问结构时,您基本上都在使用结构值的副本。
在您的示例中,您不会更改原始struct
而只会更改它的临时副本。
请参阅此处以获取更多解释:
在 .net 中,结构体实例方法在语义上等同于具有结构体类型的额外ref
参数的静态结构体方法。 因此,鉴于声明:
struct Blah {
public int value;
public void Add(int Amount) { value += Amount; }
public static void Add(ref Blah it; int Amount; it.value += Amount;}
}
该方法调用:
someBlah.Add(5);
Blah.Add(ref someBlah, 5);
在语义上是等效的,除了一个区别:仅当someBlah
是可变存储位置(变量、字段等)而不是只读存储位置或临时值(结果为读取属性等)。
这给 .net 语言的设计者带来了一个问题:不允许在只读结构上使用任何成员函数会很烦人,但他们不想允许成员函数写入只读变量。 他们决定“跳槽”,并使其在只读结构上调用实例方法将制作该结构的副本,在其上调用函数,然后将其丢弃。 这具有减慢对不写入底层结构的实例方法的调用的效果,并使得尝试使用更新只读结构上的底层结构的方法将产生与将产生不同的破坏语义如果直接传递结构,则可以实现。 请注意,在没有副本就不会正确的情况下,副本所花费的额外时间几乎永远不会产生正确的语义。
我在 .net 中的主要烦恼之一是(至少从 4.0 开始,可能是 4.5)仍然没有结构成员函数可以通过它指示它是否修改this
属性。 人们抱怨结构应该如何保持不变,而不是提供工具来允许结构安全地提供变异方法。 尽管事实上所谓的“不可变”结构是谎言。 可变存储位置中的所有非平凡值类型都是可变的,所有装箱值类型也是如此。 使结构“不可变”可能会迫使人们在只想更改一个字段时重写整个结构,但是由于struct1 = struct2
struct2 通过从 struct2 复制所有公共和私有字段来改变 struct1,并且没有任何结构的类型定义可以做来防止(除了没有任何字段)它对防止 struct 成员的意外突变没有任何作用。 此外,由于线程问题,结构在其字段之间强制执行任何类型的不变关系的能力非常有限。 恕我直言,允许任意字段访问的结构通常会更好,明确接收结构的任何代码都必须检查其字段是否满足所有必需的条件,而不是试图阻止不满足条件的结构的形成。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.