如果你对这个主题感兴趣,我有很多关于不可变编程的文章https://ericlippert.com/2011/05/26/atomicity-volatility-and-immutability-are-different-part-one/
我只是想知道为什么结构、字符串等是不可变的?
默认情况下,结构和类不是不可变的,但最好的做法是使结构不可变。我也喜欢不可变的类。
字符串是不可变的。
使它们不可变而其余对象可变的原因是什么。
使所有类型不可变的原因:
-
对不变的对象进行推理更容易。如果我有一个包含三个项目的队列,我知道它现在不是空的,五分钟前它不是空的,将来也不会是空的。它是不变的!一旦我知道了一个事实,我就可以永远使用这个事实。关于不可变对象的事实不会过时。
-
第一点的一个特例:不可变对象更容易实现线程安全。大多数线程安全问题是由于一个线程上的写入和另一个线程上的读取造成的;不可变对象没有写操作。
-
不可变对象可以拆开并重新使用。例如,如果您有一棵不可变的二叉树,那么您可以将其左右子树用作 不同 树的子树,而无需担心。在可变结构中,您通常最终会制作数据副本以重新使用它,因为您不希望对一个逻辑对象的更改影响另一个逻辑对象。这可以节省大量时间和内存。
使结构不可变的原因
使结构不可变的原因有很多。这里只有一个。
结构是按值复制的,而不是按引用复制的。很容易意外地将结构视为通过引用复制。例如:
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 ...
}
错了!那应该是 (ref S s) - 如果你不这样做,那么突变将发生在 s 的 copy 上。如果你一开始就不允许突变,那么所有这些问题都会消失。
使字符串不可变的原因
还记得我关于不可变结构保持事实的第一点吗?
假设字符串是可变的:
public static File OpenFile(string filename)
{
if (!HasPermission(filename)) throw new SecurityException();
return InternalOpenFile(filename);
}
如果恶意调用者在安全检查之后 和文件打开之前之前 改变文件名怎么办?该代码刚刚打开了一个他们可能没有权限的文件!
同样,可变数据很难推理。您希望“此调用者有权查看此字符串描述的文件”这一事实永远为真,而不是直到发生突变。对于可变字符串,要编写安全代码,我们必须不断地制作我们知道不会更改的数据副本。
什么是使对象不可变的因素?
类型在逻辑上是否代表“永恒”的价值?数字 12 是数字 12;它没有改变。整数应该是不可变的。点 (10, 30) 就是点 (10, 30);它没有改变。点应该是不可变的。字符串“abc”就是字符串“abc”;它没有改变。字符串应该是不可变的。列表 (10, 20, 30) 不会改变。以此类推。
有时类型代表确实发生变化的事物。玛丽史密斯的姓是史密斯,但明天她可能是玛丽琼斯。或者今天的史密斯小姐明天可能是史密斯医生。外星人现在有 50 点生命值,但在被激光束击中后有 10 点。有些东西最好用突变来表示。
可变和不可变对象的内存分配和释放方式有什么不同吗?
并非如此。正如我之前提到的,关于不可变值的好处之一是您可以重复使用它们的一部分而无需复制。所以从这个意义上说,内存分配可能非常不同。