【发布时间】:2014-10-25 10:12:26
【问题描述】:
我最近阅读了很多关于Value Types & Reference Types 及其差异的文献。本题围绕值类型的可变性和不变性主题。
根据我的阅读,.NET 中的值类型似乎应该以不可变的方式编写;也就是说,一旦为它们分配了一个值,该类型在内存中的值就永远不会改变。只有该类型的后续副本才能在内存中使用基于原始值的新值构造新实例。 .NET 中的可变性似乎是邪恶的。
为了阐明对不可变性的理解(为了我自己的理智,为了他人),我在下面展示了这一点:
DateTime 和 TimeSpan 是 不可变 结构的示例,因为一旦将值分配给实例,该实例值就无法更改,这通过只读属性很明显:
DateTime dt = new DateTime();
DateTime newdt = dt.AddDays(2); // Okay, new value stored in newdt
newdt.Year = 1945; // Error, cannot write to readonly property
然而,在查看 Int32、Double 或 Char 等原始类型时,不变性可能会令人困惑,因为这些类型似乎是可变的,但我的直觉是,实际上,不变性是通过 CLR 透明地处理的;以以下操作为例(我已经在一些非常基本的 x86 等效项中进行了评论,以了解如何根据原始类型处理不变性)
int x = 0;
// xor eax, eax; 'clear register to 0
// push eax; 'push eax (0) onto the stack
x = 5;
// pop eax; 'pop stack (0) into eax
// mov eax, 5; 'eax = 5
// push eax; 'push eax (5) onto the stack
x++;
// pop eax; 'pop stack (5) into eax
// add eax, 1; 'eax = 5 + 1
// push eax; 'push eax (6) onto the stack
到目前为止一切顺利;微软似乎在将不变性实现到其值类型中做得很好。但随后我们开始发现坏苹果,以及使可变性看起来不错的细微差别,并使开发人员产生一种虚假的安全感!
我说的是System.Drawing 命名空间中的Point、Size、Rectangle(以及其他一些)。
突然之间,我们获得了通过属性改变值类型的能力,我有一个理论来解释为什么这是可能的;以下面的代码为例
Point p = new Point();
p.X = 100;
p.Y = 200;
// Immutability (had it been implemented here) might infer the following usage
Point p = new Point(100, 200);
Point p2 = p.AddXY(200, 300);
但是如前所述,我有一个关于为什么这些结构是可变的理论:
- Microsoft 只是想让这些结构的使用变得更容易。
- 它们可与本机 GDI/GDI+ 调用互操作,因此它们的行为是围绕其本机 C/C++ 对应物设计的。
最后是我的问题:
- 我是否完全涵盖并理解了不变性和可变性?
- 开发人员是否应该将构建不可变结构作为一项规则?
- 什么时候可以构建可变结构?
【问题讨论】:
-
google 上有很多关于这方面的内容:google.at/search?q=c%23+struct+mutability+consistency
-
@ErenErsönmez 我读过那篇文章,但即便如此,我还是对为什么可变性可能被视为设计决策而不仅仅是“邪恶”提出了很多意见。
-
我发现变异 methods 有问题。仅仅使用 setter 来拥有公共可变字段或属性并不是什么大问题。
标签: c# .net struct immutability mutable