Mik*_*ike 58 c# struct immutability
这是声明不可变结构的正确方法吗?
public struct Pair
{
public readonly int x;
public readonly int y;
// Constructor and stuff
}
Run Code Online (Sandbox Code Playgroud)
我想不出为什么这会遇到问题,但我只想问一下.
在这个例子中,我使用了int.如果我使用了一个类,但该类也是不可变的,就像这样呢?这应该也可以正常工作,对吧?
public struct Pair
{
public readonly (immutableClass) x;
public readonly (immutableClass) y;
// Constructor and stuff
}
Run Code Online (Sandbox Code Playgroud)
(旁白:据我所知,使用性能更加普及,并允许改变,但这种结构的目的是从字面上只存储两个值我在永恒的问题只是有兴趣在这里.)
Eri*_*ert 114
如果您要使用结构,最佳做法是使它们不可变.
使所有字段只读是一种很好的方法来帮助(1)记录结构是不可变的,以及(2)防止意外突变.
然而,有一个皱纹,这实际上是一个奇怪的巧合,我计划在下周写博客.那就是:readonly在struct字段上是一个谎言.人们期望只读字段不能改变,但当然可以.结构字段上的"readonly"是在帐户中没有钱的情况下编写支票的声明.结构体不拥有其存储空间,并且存储体可以变异.
例如,让我们采取你的结构:
public struct Pair
{
public readonly int x;
public readonly int y;
public Pair(int x, int y)
{
this.x = x;
this.y = y;
}
public void M(ref Pair p)
{
int oldX = x;
int oldY = y;
// Something happens here
Debug.Assert(x == oldX);
Debug.Assert(y == oldY);
}
}
Run Code Online (Sandbox Code Playgroud)
是否有任何事情可能发生在"此处发生的事情"导致调试断言被违反?当然.
public void M(ref Pair p)
{
int oldX = this.x;
int oldY = this.y;
p = new Pair(0, 0);
Debug.Assert(this.x == oldX);
Debug.Assert(this.y == oldY);
}
...
Pair myPair = new Pair(10, 20);
myPair.M(ref myPair);
Run Code Online (Sandbox Code Playgroud)
现在会发生什么?断言被违反了!"this"和"p"指的是相同的存储位置.存储位置发生了变异,因此"this"的内容发生了变异,因为它们是相同的.结构不能强制执行x和y的只读,因为结构不拥有存储; 存储是一个局部变量,可以根据需要随意变异.
您不能依赖于从未观察到结构中的只读字段发生变化的不变量; 您唯一可以依赖的是您无法编写直接更改它的代码.但是有了这样一个偷偷摸摸的工作,你可以间接改变它你想要的一切.
另见Joe Duffy关于此问题的优秀博客文章:
http://joeduffyblog.com/2010/07/01/when-is-a-readonly-field-not-readonly/
从 C# 7.2 开始,您现在可以将整个结构声明为不可变的:
public readonly struct Pair
{
public int x;
public int y;
// Constructor and stuff
}
Run Code Online (Sandbox Code Playgroud)
这与将所有字段标记为 具有相同的效果readonly,并且还将向编译器本身证明该结构是不可变的。这将通过减少编译器制作的防御性副本的数量来提高使用结构体的区域的性能。
正如Eric Lippert 的回答中所指出的,这并不能阻止结构本身完全重新分配,从而提供其字段从您下方改变的效果。可以使用按值传递或使用新的in参数修饰符来帮助防止这种情况:
public void DoSomething(in Pair p) {
p.x = 0; // illegal
p = new Pair(0, 0); // also illegal
}
Run Code Online (Sandbox Code Playgroud)