我正在阅读Eric Liperts关于Mutating Readonly Structs的博客,我在这篇博客中看到很多参考资料作为一个论证,为什么价值类型必须是不可变的。 但仍然有一点不清楚,说当你访问值类型时,你总是得到它的副本,这是一个例子:
struct Mutable
{
private int x;
public int Mutate()
{
this.x = this.x + 1;
return this.x;
}
}
class Test
{
public readonly Mutable m = new Mutable();
static void Main(string[] args)
{
Test t = new Test();
System.Console.WriteLine(t.m.Mutate());
System.Console.WriteLine(t.m.Mutate());
System.Console.WriteLine(t.m.Mutate());
}
}
这就是为什么我改变
的原因public readonly Mutable m = new Mutable();
到
public Mutable m = new Mutable();
一切都开始起作用预期。
请您更清楚地解释为什么值类型必须是不可变的。 我知道它对线程安全有好处,但在这种情况下,同样可以应用于引用类型。
答案 0 :(得分:4)
使用变异方法的结构在几种情况下表现得很奇怪。
您已经发现的示例是一个只读字段。防御性副本是必要的,因为您不想改变只读字段。
但也用作属性。再次发生隐式复制,只复制副本。即使该物业有一个二传手。
struct Mutable
{
private int x;
public int Mutate()
{
this.x = this.x + 1;
return this.x;
}
}
Mutable property{get;set;}
void Main()
{
property=new Mutable();
property.Mutate().Dump();//returns 1
property.Mutate().Dump();//returns 1 :(
}
这表明变异方法在结构上是有问题的。但它没有表明具有公共字段的可变结构或具有setter的属性存在问题。
答案 1 :(得分:2)
线程安全是一个明确的技术原因。它适用于值类型以及引用类型(请参阅System.String)。
更一般的指导方针“价值类型应该是不可变的”是不同的。它是关于代码的可读性,主要来自可变值可能导致的混淆。此代码段只是一个示例。大多数人不会期望1,1,1结果。
答案 2 :(得分:2)
我不知道C#所以我会尝试回答你问题的第二部分。
为什么值类型必须是不可变的?
Domain Driven Design的观点有两种类型的对象:
如果值类型是可变的,那么想象如果有可能改变第二个值的话会发生什么:2 == 1 + 1不能保证是真的。
请参阅以下链接了解更多信息:
答案 3 :(得分:0)
我认为关于这个例子的棘手问题是人们可能认为它不应该是可能的。你创建了一个只读的Mutable实例,但你可以通过Mutate()函数改变它的值,因此在某种意义上违反了不变性的概念。但是,严格来说,它的工作原理是因为私有字段x不是只读的。如果你在可变类中做了一个简单的改变,那么实际上就会强制实现不变性:
private readonly int x;
然后Mutate()函数将产生编译器错误。
该示例清楚地显示了copy-by-value如何在只读变量的上下文中工作。无论何时调用m,您都要创建实例的副本,而不是实例引用的副本 - 如果Mutable是类而不是结构,则会发生后者。
因为每次调用m时你都会调用1)实例的副本,以及2)只读实例的副本,x的值在总是为0时复制发生。当你在副本上调用Mutate()时,它会将x递增到1,这是因为x本身不是只读的。但是下次调用Mutate()时,你仍然会调用默认值为0.正如他在文章中所说的那样“m是不可变的,但副本不是”。原始实例的每个副本都将x设置为0,因为正在复制的对象永远不会更改,而其副本可以更改。
也许这有帮助。