C# 结构的不变性

C# 结构的不变性,c#,.net,struct,immutability,C#,.net,Struct,Immutability,可能重复: 我在很多地方都读到过,包括这里,最好让结构保持不变 这背后的原因是什么?我看到很多微软创建的结构是可变的,比如xna中的结构。可能在BCL中还有更多 不遵循此指南的利弊是什么?结构应该表示值。值不会改变。数字12是永恒的 但是,请考虑: Foo foo = new Foo(); // a mutable struct foo.Bar = 27; Foo foo2 = foo; foo2.Bar = 55; 现在foo.Bar和foo2.Bar是不同的,这常常出乎意料。特别是在诸如

可能重复:

我在很多地方都读到过,包括这里,最好让结构保持不变

这背后的原因是什么?我看到很多微软创建的结构是可变的,比如xna中的结构。可能在BCL中还有更多


不遵循此指南的利弊是什么?

结构应该表示值。值不会改变。数字12是永恒的

但是,请考虑:

Foo foo = new Foo(); // a mutable struct
foo.Bar = 27;
Foo foo2 = foo;
foo2.Bar = 55;
现在foo.Bar和foo2.Bar是不同的,这常常出乎意料。特别是在诸如属性之类的场景中(幸运的是编译器检测到了这一点)。还有收藏等;你是如何明智地变异它们的


对于可变结构,数据丢失太容易了。

当您复制周围的结构时,会复制其内容,因此如果您修改复制的版本,“原始”将不会更新

这是错误的来源,因为即使您知道自己落入了复制结构(仅通过将结构传递给方法)和修改副本的陷阱

上周又发生在我身上,让我找了一个小时的虫子

保持结构不变可以防止


除此之外,您需要首先确保您有一个真正好的理由使用结构——“优化”或“我想要在堆栈上快速分配的东西”不算作答案。封送或依赖于布局的东西-好的,但通常不应该将这些结构保留很长时间,它们是值而不是对象。

应该使结构不可变的原因是它们是不可变的,这意味着每次将它们传递给方法时都会复制它们

因此,例如,如果您有一个返回结构的属性,那么修改该结构上某个字段的值将毫无价值,因为getter将返回该结构的副本,而不是对该结构的引用。我在代码中看到过这种情况,通常很难捕捉到


如果您设计的结构是不变性的,那么您可以帮助程序员避免这些错误

最大的缺点是事物的行为与您期望的不同——特别是当可变性来自于直接值和其中的引用类型的混合时

老实说,我已经记不清人们在新闻组中使用可变结构时遇到的所有奇怪问题了,但这些原因确实存在。可变结构会导致问题。走开

编辑:我刚刚找到一封关于这个话题的电子邮件。它只阐述了一点:

  • 这在哲学上是错误的:结构应该代表某种基本价值。这些基本上是不变的。你不必改变数字5。可以将变量的值从5更改为6,但从逻辑上讲,不能更改值本身

  • 这实际上是一个问题:它造成了许多奇怪的情况。如果它通过一个接口是可变的,那就特别糟糕了。然后可以开始更改装箱的值。哎呀。我看到过很多新闻组的帖子,都是由于人们试图使用可变结构而遇到问题。我看到了一个非常奇怪的LINQ示例,它失败了,因为
    List.Enumerator
    就是一个结构


结构通常应表示某种单一的统一体。因此,更改值的一个属性没有多大意义,如果您想要一个不同的值,则创建一个全新的值更有意义

当使用不可变结构时,语义变得更简单,并且可以避免如下陷阱:

// a struct
struct Interval {
   int From { get; set; }
   int To { get; set; }
}

// create a list of structs
List<Interval> intervals = new List<Interval>();

// add a struct to the list
intervals.Add(new Interval());

// try to set the values of the struct
intervals[0].From = 10;
intervals[0].To = 20;
//结构
结构间隔{
来自{get;set;}的int
int到{get;set;}
}
//创建一个结构列表
列表间隔=新列表();
//将结构添加到列表中
添加(新的Interval());
//尝试设置结构的值
间隔[0]。从=10;
间隔[0]。至=20;
结果是列表中的结构根本没有更改。表达式间隔[0]从列表中复制结构的值,然后更改临时值的属性,但该值永远不会放回列表中


编辑:将示例更改为使用列表而不是数组。

没有比可变结构更便宜的操作了,这就是为什么您经常在高性能代码(如图形处理例程)中看到它的原因

不幸的是,可变结构不能很好地处理对象和属性,修改结构副本而不是结构本身太容易了。因此,它们不适用于大多数代码


另外,为了避免复制可变结构的成本,它们通常以数组的形式存储和传递。

技术原因是可变结构似乎能够完成它们实际上没有完成的事情。由于设计时语义与引用类型相同,这会使开发人员感到困惑。此代码:

public void DoSomething(MySomething something)
{
    something.Property = 10;
}
根据
MySomething
struct
还是
class
的不同,行为会有很大的不同。对我来说,这是一个令人信服的理由,但不是最令人信服的理由。如果查看的话,您可以看到结构应该如何处理的关系。DDD中的值对象可以最好地表示为.Net中的值类型(因此也是结构)。因为它没有身份,所以不能改变


从你的地址这样的角度来考虑这个问题。你可以“改变”你的地址,但地址本身没有改变。事实上,你有一个新的地址分配给你。从概念上讲,这是可行的,因为如果你真的改变了地址,你的室友也必须搬家。

你已经询问了不遵循结构应该是不可变的原则的利弊

缺点:这些缺点在现有的答案中都有很好的说明,并且所描述的大多数问题都是由于相同的原因造成的-意外