对不可变结构使用公共只读字段是否有效?
这是声明不可变结构的正确方法吗?
public struct Pair
{
public readonly int x;
public readonly int y;
// Constructor and stuff
}
我想不出为什么这会遇到问题,但我只是想确认一下。
在这个例子中,我使用了整数。如果我改用一个类,但该类也是不可变的,就像这样呢?那应该也可以正常工作,对吧?
public struct Pair
{
public readonly (immutableClass) x;
public readonly (immutableClass) y;
// Constructor and stuff
}
(旁白:我知道使用 Properties 更通用并且允许更改,但是这个结构实际上只是存储两个值。我只是对这里的不变性问题感兴趣。)
Is this a proper way to declare immutable structs?
public struct Pair
{
public readonly int x;
public readonly int y;
// Constructor and stuff
}
I can't think of why this would run into problems, but I just wanted to ask to make sure.
In this example, I used ints. What if I used a class instead, but that class is also immutable, like so? That should work fine too, right?
public struct Pair
{
public readonly (immutableClass) x;
public readonly (immutableClass) y;
// Constructor and stuff
}
(Aside: I understand that using Properties is more generalizable and allows changing, but this struct is intended literally to just store two values. I'm just interested in the immutability question here.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您要使用结构,最佳实践是使它们不可变。
将所有字段设置为只读是一个很好的方法,可以帮助 (1) 记录结构是不可变的,以及 (2) 防止意外突变。
然而,有一个问题,实际上是一个奇怪的巧合,我正计划下周在博客上讨论这个问题。也就是说:结构体字段上的只读是一个谎言。人们期望只读字段不能更改,但它当然可以。结构体字段上的“readonly”是在其帐户中没有钱的情况下写支票的声明。 结构不拥有自己的存储,并且该存储可以发生变化。
例如,让我们以您的结构为例:
在“此处发生某事”时是否会发生任何导致调试断言的事情被侵犯?当然。
现在会发生什么?主张已被违反! “this”和“p”指的是相同的存储位置。存储位置发生了变化,因此“this”的内容也发生了变化,因为它们是相同的东西。该结构无法强制 x 和 y 的只读性,因为该结构不拥有存储;存储是一个局部变量,可以随意改变。
您不能依赖结构中的只读字段永远不会发生变化的不变性;您唯一可以依赖的是您无法编写直接更改它的代码。但通过像这样的一些偷偷摸摸的工作,你可以间接地改变你想要的一切。
另请参阅 Joe Duffy 关于此问题的优秀博客文章:
http:// joeduffyblog.com/2010/07/01/when-is-a-readonly-field-not-readonly/
If you're going to use structs, it is a best practice to make them immutable.
Making all the fields readonly is a great way to help (1) document that the struct is immutable, and (2) prevent accidental mutations.
However, there is one wrinkle, which actually in a strange coincidence I was planning on blogging about next week. That is: readonly on a struct field is a lie. One expects that a readonly field cannot change, but of course it can. "readonly" on a struct field is the declaration writing cheques with no money in its account. A struct doesn't own its storage, and it is that storage which can mutate.
For example, let's take your struct:
Is there anything that can happen at "something happens here" that causes the debug assertions to be violated? Sure.
And now what happens? The assertion is violated! "this" and "p" refer to the same storage location. The storage location is mutated, and so the contents of "this" are mutated because they are the same thing. The struct is not able to enforce the read-only-ness of x and y because the struct doesn't own the storage; the storage is a local variable that is free to mutate as much as it wants.
You cannot rely on the invariant that a readonly field in a struct is never observed to change; the only thing you can rely on is that you can't write code that directly changes it. But with a little sneaky work like this you can indirectly change it all you want.
See also Joe Duffy's excellent blog article on this issue:
http://joeduffyblog.com/2010/07/01/when-is-a-readonly-field-not-readonly/
从 C# 7.2 开始,您现在可以将整个结构声明为不可变:
这与将所有字段标记为只读具有相同的效果,并且还将向编译器本身记录该结构是不可变的。这将通过减少编译器创建的防御副本的数量来提高使用该结构的区域的性能。
正如 Eric Lippert 的回答中所述,这并不能阻止结构本身被完全重新分配,从而提供其结构的效果领域从你的脚下发生变化。按值传递或使用新的
in
参数修饰符都可以帮助防止这种情况:As of C# 7.2, you can now declare an entire struct as immutable:
This will have the same effect as marking all of the fields as
readonly
, and will also document to the compiler itself that the struct is immutable. This will increase the performance of areas where the struct is used by reducing the number of defensive copies the compiler makes.As noted in Eric Lippert's answer, this does not prevent the structure itself from being reassigned completely, and thus providing the effect of its fields changing out from under you. Either passing by value or using the new
in
parameter modifier can be used to help prevent this:这确实会使其成为不可变的。我想你最好添加一个构造函数。
如果它的所有成员也是不可变的,这将使其完全不可变。这些可以是类或简单值。
That would make it immutable indeed. I suppose you better add a constructor though.
If all its members are immutable too, this would make it entirely immutable. These can be classes or simple values.
编译器将禁止分配给
readonly
字段以及只读属性。我建议使用只读属性主要是出于公共接口原因和数据绑定(这不适用于字段)。如果这是我的项目,我会要求结构/类是否是公共的。如果它是程序集的内部或类的私有属性,我可以首先忽略它,然后将它们重构为只读属性。
The compiler will forbid assignment to
readonly
fields as well as read-only properties.I recommend using read-only properties mostly for public interface reasons and data-binding (which won't work on fields). If it were my project I would require that if the struct/class is public. If it's going to be internal to an assembly or private to a class, I could overlook it at first and refactor them to read-only properties later.