众所周知,String是不可变的。 String不可变的原因是什么以及引入字符串生成器类是可变的?
- 不可变类型的实例本质上是线程安全的,因为没有线程可以修改它,所以消除了线程以干扰另一个线程的方式修改它的风险(引用本身是另一回事)。
- 类似地,别名不能产生更改(如果 x 和 y 都引用同一对象,则对 x 的更改必然对 y 进行更改)这一事实允许进行相当大的编译器优化。
- 节省内存的优化也是可能的。实习和原子化是最明显的例子,尽管我们可以做相同原理的其他版本。我曾经通过比较不可变对象并替换对重复项的引用,使它们都指向同一个实例,节省了大约半 GB 的内存(这很耗时,但为了节省大量内存而需要额外启动一分钟)在相关案例中性能获胜)。对于可变对象,这是无法完成的。
- 将不可变类型作为方法传递给参数不会产生任何副作用,除非它是
out
or ref
(因为这改变了引用,而不是对象)。因此,程序员知道如果string x = "abc"
在方法的开头,并且方法体中不会改变,那么x == "abc"
在该方法的末尾。
- 从概念上讲,语义更像是值类型;特别是,平等是基于国家而不是身份。这意味着
"abc" == "ab" + "c"
。虽然这不需要不变性,但事实上,对此类字符串的引用在其整个生命周期中始终等于“abc”(这确实需要不变性),这使得在保持与先前值的相等性至关重要的情况下用作键,更容易确保正确性of (字符串确实常用作键)。
- 从概念上讲,不可变更有意义。如果我们在圣诞节后加一个月,我们并没有改变圣诞节,而是在一月下旬制定了一个新的日期。因此这是有道理的
Christmas.AddMonths(1)
产生一个新的DateTime
而不是改变一个可变的。 (另一个例子,如果我作为一个可变对象更改了我的名字,改变的是我正在使用的名字,“Jon”保持不变,其他 Jons 将不受影响。
- 复制快速而简单,只需创建一个克隆
return this
。由于副本无论如何都无法更改,因此假装某些内容是其自己的副本是安全的。
- [编辑,我忘了这个]。内部状态可以在对象之间安全地共享。例如,如果您正在实现由数组、起始索引和计数支持的列表,那么创建子范围最昂贵的部分将是复制对象。但是,如果它是不可变的,则子范围对象可以引用同一数组,只需更改起始索引和计数,并使用very施工时间发生显着变化。
总而言之,对于那些不以变化为目的的对象来说,不可变可能有很多优点。主要缺点是需要额外的构造,尽管即使在这里它也经常被夸大(记住,在 StringBuilder 变得比等效的串联系列及其固有构造更高效之前,您必须执行多次追加)。
如果可变性是对象目的的一部分(谁希望由工资永远不会改变的 Employee 对象建模),这将是一个缺点,尽管有时即使这样它也可能很有用(在许多网络和其他无状态对象中)应用程序中,执行读取操作的代码与执行更新的代码是分开的,并且使用不同的对象可能是自然的 - 我不会使对象不可变,然后强制该模式,但如果我已经有了该模式,我可能会创建“读取”对象对于性能和正确性保证增益来说是不可变的)。
写时复制是一个中间立场。这里“真实”类持有对“状态”类的引用。状态类在复制操作中共享,但如果更改状态,则会创建状态类的新副本。这在 C++ 中比在 C# 中更常使用,这就是为什么 std:string 享有不可变类型的一些(但不是全部)优点,同时保持可变。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)