可能的重复:
为什么可变结构是邪恶的?

我在很多地方读过它,包括这里,最好使结构不可变。

这背后的原因是什么?我看到很多 Microsoft 创建的结构体都是可变的,就像 xna 中的结构体一样。BCL中可能还有更多。

不遵循本指南有哪些优点和缺点?

有帮助吗?

解决方案

结构应代表。值不会改变。 12号是永恒的。

但是,请考虑:

Foo foo = new Foo(); // a mutable struct
foo.Bar = 27;
Foo foo2 = foo;
foo2.Bar = 55;

现在foo.Bar和foo2.Bar是不同的,这通常是意料之外的。特别是在类似属性的场景中(幸运的是编译器检测到了这一点)。还有收藏品等;你怎么理智地改变他们?

使用可变结构,数据丢失太容易了。

其他提示

最大的缺点是事情不会按照您期望的方式运行 - 特别是当可变性来自直接值和其中的引用类型的混合时。

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

编辑:我刚刚找到了一封我不久前写的关于这个主题的电子邮件。它稍微详细说明了一点:

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

  • 这实际上是一个问题:它创造了很多奇怪的情况。如果它是通过接口可变的,那就特别糟糕。然后您可以开始更改盒装值。恶心。我看过很多新闻组帖子,这些帖子是由于人们尝试使用可变结构并遇到问题而引起的。我看到一个非常奇怪的 LINQ 示例,它失败了,因为 List<T>.Enumerator 例如,是一个结构体。

我经常在我的(性能关键)项目中使用可变结构 - 而且我没有遇到问题,因为我理解了复制语义的含义。据我所知,人们提倡不可变结构的主要原因是了解其含义的人不能让自己陷入困境。

这不是一件可怕的事情 - 但是我们现在处于危险状态,因为它变成了福音真理<!>,而实际上有时它是合法地成为最佳选择struct mutable。与所有事情一样,规则也有例外。

操作比可变结构更便宜,这就是为什么你经常在高性能代码中看到它,比如图形处理程序。

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

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

技术原因是可变结构出现以便能够做他们实际上没做的事情。由于设计时语义与引用类型相同,因此开发人员会感到困惑。这段代码:

public void DoSomething(MySomething something)
{
    something.Property = 10;
}

根据MySomethingstruct还是class,行为完全不同。对我来说,这是一个引人注目的,但不是最引人注目的原因。如果你看一下 DDD 值对象,您可以看到应该如何处理结构的连接。 DDD中的值对象最好表示为.Net中的值类型(因此也就是结构)。因为它没有身份,所以无法改变。

根据您的地址等方面考虑这一点。您可以<!>“更改<!>”;你的地址,但地址本身没有改变。实际上,您已为您分配了新地址。从概念上讲,这是有效的,因为如果您实际上更改了您的地址,您的室友也必须移动。

您已根据指南提出 not 的优缺点,结构应该是不可变的。

缺点:现有答案中很好地涵盖了缺点,并且所描述的大多数问题都是由于相同的原因 - 由于结构的价值语义而导致的意外行为

优点:使用可变结构的主要人员可以是性能。显然,这个建议伴随着关于优化的所有常见警告:确保优化部分代码 并确保任何更改通过分析实际优化代码的性能。

有关讨论何时可能需要使用可变结构的优秀文章,请参阅Rico Mariani的基于价值的计划的绩效测验(或更具体地,答案)。

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

使用不可变结构时语义变得更简单,你可以避免这样的陷阱:

// 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;

结果是列表中的结构根本没有改变。表达式Interval [0]从列表中复制结构的值,然后更改临时值的属性,但该值永远不会放回列表中。

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

复制结构时,复制其内容,因此如果修改复制版本,则<!> quot;原始<!>将不会更新。

这是错误的来源,因为即使您知道自己陷入了复制结构的陷阱(只需将其传递给方法)并修改副本。

上周发生了再次,让我花了一个小时寻找一个错误。

保持结构不可变会阻止......

除此之外,你需要确保你有一个非常好的理由首先使用结构 - <!> quot; optimization <!> quot;或者<!>我想要在堆栈上快速分配的东西<!>不算作答案。编组或你依赖布局的东西 - 好吧,但你通常不应该长时间保留这些结构,它们是值而不是对象。

你应该使结构不可变的原因是它们是 ValueTypes ,表示每次将它们传递给方法时都会复制它们。

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

如果你设计你的结构不变,你可以帮助程序员避免这些错误。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top