为什么C#结构是不可变的?

我只是想知道为什么结构,string等是不可改变的? 是什么原因使他们不可变,其余的对象是可变的。 什么是被认为使一个对象不变的东西?

内存分配和释放可变和不可变对象的方式有什么区别吗?

如果这个话题引起你的兴趣,我可以在http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/find许多关于不可变编程的文章。;

我只是想知道为什么结构,string等是不可改变的?

结构和类默认情况下不是不可变的,尽pipe使结构不变是一个最佳实践。 我也喜欢不可变的类。

string是不可改变的。

是什么原因使他们不可变,其余的对象是可变的。

使所有types不变的原因:

  • 对于不改变的对象推理是比较容易的。 如果我有一个有三个项目的队列,我知道现在不是空的,五分钟之前不是空的,将来也不会是空的。 这是不可改变的! 一旦我知道这个事实,我可以永远使用这个事实。 有关不变对象的事实不会过时。

  • 第一点的特例:不可变的对象更容易制作线程安全。 大多数线程安全问题是由于在一个线程上写入并读取另一个线程所致。 不可变的对象没有写入。

  • 不可变的对象可以分解和重用。 例如,如果你有一个不可变的二叉树,那么你可以使用它的左右子树作为不同树的子树,而不用担心它。 在一个可变的结构中,通常最终会复制数据以重用它,因为您不希望更改一个逻辑对象而影响另一个逻辑对象。 这可以节省大量的时间和内存。

理由使结构不可改变

有很多原因使得结构不可变。 这只是一个。

结构是通过值复制的,而不是通过引用。 意外地将一个结构视为通过引用复制是很容易的。 例如:

 void M() { S s = whatever; ... lots of code ... s.Mutate(); ... lots more code ... Console.WriteLine(s.Foo); ... } 

现在你想把这些代码重构成一个辅助方法:

 void Helper(S s) { ... lots of code ... s.Mutate(); ... lots more code ... } 

错误! 这应该是(参考S) – 如果你不这样做,那么突变将发生在一个副本 。 如果你一开始不允许突变,那么所有这些问题都会消失。

使string不可变的原因

请记住我关于不变结构事实的第一点吗?

假设string是可变的:

 public static File OpenFile(string filename) { if (!HasPermission(filename)) throw new SecurityException(); return InternalOpenFile(filename); } 

如果恶意主叫方在安全检查之后和打开文件之前突变文件名该怎么办? 该代码只是打开一个文件,他们可能没有权限!

再次,可变数据很难推理。 你希望事实“这个调用者被授权查看由这个string描述的文件” 永远是真的, 直到一个变异发生 。 有了可变string,为了编写安全的代码,我们必须不断地复制我们知道不会改变的数据。

什么是被认为使一个对象不变的东西?

这种types在逻辑上是否代表了一种“永恒”价值? 12号是12号; 它不会改变。 整数应该是不变的。 点(10,30)是点(10,30)。 它不会改变。 点应该是不可改变的。 string“abc”是string“abc”; 它不会改变。 string应该是不可变的。 列表(10,20,30)不会改变。 等等。

有时候,这个types代表着变化的东西。 玛丽·史密斯的姓氏是史密斯,但明天她可能是玛丽·琼斯。 或者今天史密斯小姐明天可能是史密斯医生。 这个外星人现在有50个生命点,但有10个被激光束击中之后。 有些事情是最好的代表作为突变。

内存分配和释放可变和不可变对象的方式有什么区别吗?

不是这样的。 正如我之前提到的,关于不可变值的好处之一就是可以在不复制副本的情况下重新使用其中的一部分。 所以从这个意义上讲,内存分配可能会非常不同。

结构不……这就是为什么可变结构是邪恶的。

创build可变结构可能会导致应用程序中出现各种奇怪的行为,因此它们被认为是一个非常糟糕的主意(源于它们看起来像引用types,但实际上是一个值types,只要你通过就会被复制他们周围)。

另一方面,弦是。 这使得它们本质上是线程安全的,并允许通过string实例进行优化。 如果你需要构build一个复杂的string,你可以使用StringBuilder

当适用于结构和类时,可变性和不变性的概念有不同的含义。 可变类的一个关键方面(通常是关键弱点)是,如果Foo有一个List<Integer>types的Bar ,它包含一个包含(1,2,3)的列表的引用,其他代码引用同样的列表可以修改它,这样Bar可以引用包含(4,5,6)的列表, 即使其他代码没有访问Bar 。 相反,如果Foo有一个types为System.Drawing.Point的字段Biz ,则任何可以修改Biz任何方面的唯一方法就是具有对该字段的写入权限

结构的字段(公共和私有)可以通过任何可以改变存储该结构的存储位置的代码进行变异,并且不能被任何不能改变其存储位置的代码进行变异。 如果封装在结构中的所有信息都保存在其字段中,那么这样的结构可以有效地将不可变types的控制与可变types的方便性结合起来,除非结构以这种方式编码,不幸的是一些微软程序员推荐的习惯)。

结构的“问题”是当一个方法(包括一个属性实现)在只读上下文(或不可变位置)的结构上被调用时,系统复制结构,在临时副本上执行方法,并静默丢弃结果。 这种行为导致程序员提出了一个不幸的概念,即避免变异方法出现问题的方法是让许多结构不允许分段更新, 只要用暴露字段replace属性就能更好地避免问题发生。

顺便说一下,有些人抱怨说,当一个类属性返回一个方便可变的结构体时,对结构体的改变不会影响它所来自的类。 我会认为这是一件好事 – 返回的项目是一个结构的事实使行为清晰(特别是如果它是一个暴露的字段结构)。 在Drawing.Matrix上使用一个假想的结构和属性的片段与使用Microsoft实现的该类的实际属性进行比较:

 //假设的结构
公共结构{
  公共浮动xx,xy,yx,yy,dx,dy;
 } Transform2d;

 //“System.Drawing.Drawing2d.Matrix”的假设属性
 public Transform2d Transform {get;}

 //“System.Drawing.Drawing2d.Matrix”的实际属性
 public float []元素{get;  }

 //使用假设结构的代码
 Transform2d myTransform = myMatrix.Transform;
 myTransform.dx + = 20;
 ...使用myTransform的其他代码

 //使用实际的Microsoft属性进行编码
 float [] myArray = myMatrix.Elements;
 myArray [4] + = 20;
 ...使用myArray的其他代码

看看实际的Microsoft属性,有没有什么办法来判断写入myArray[4]是否会影响myMatrix ? 即使在页面http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx有什么办法可以告诉?; 如果财产是用基于结构的等价物来写的,那么就不会有混淆。 返回该结构的属性将返回没有更多也不less于六个数字的现值。 更改myTransform.dx只不过是写入一个浮点variables而没有附加任何东西。 任何人不喜欢改变myTransform.dx不影响myMatrix应该同样令人讨厌,写myArray[4]也不会影响myMatrix ,除了myMatrixmyTransform的独立性是显而易见的,而独立性myMatrixmyArray不是。

结构types不是不可变的。 是的,string是。 让你自己的types不可变是简单的,不要提供一个默认的构造函数,使所有的字段都是私有的,并且没有定义改变字段值的方法或属性。 有一个方法,应该改变对象而不是返回一个新的对象。 有一个memory management的angular度,你往往会创造大量的副本和垃圾。

结构可以是可变的,但它是一个坏主意,因为它们具有复制语义。 如果你改变了一个结构,你可能实际上正在修改一个副本。 跟踪到底发生了什么变化是非常棘手的。

可变的结构滋生了错误。