众所周知,String是不变的.字符串是不可变的,StringBuilder类作为可变类引入的原因是什么?

推荐答案

  1. 不可变类型的实例本质上是线程安全的,因为没有线程可以修改它,所以一个线程以干扰另一个线程的方式修改它的风险被消除(引用本身是另一回事).
  2. 类似地,别名不能产生更改的事实(如果x和y都引用同一对象,则x的更改会导致y的更改),这允许进行相当大的编译器优化.
  3. 内存节省优化也是可能的.实习和原子化是最明显的例子,尽管我们可以做相同原则的其他版本.有一次,我通过比较不可变对象和替换对重复对象的引用,使它们都指向同一个实例,节省了大约半GB的内存(这很费时,但一分钟的额外启动以节省大量内存,这是本 case 中的性能胜利).使用无法完成的可变对象.
  4. 将不可变类型作为方法传递给参数不会产生任何副作用,除非它是outref(因为这会更改引用,而不是对象).因此,程序员知道,如果string x = "abc"在方法的开头,而这在方法的主体中没有改变,那么x == "abc"在方法的结尾.
  5. 从概念上讲,语义更像是值类型;尤其是平等是基于国家而非身份的.这意味着"abc" == "ab" + "c".虽然这并不需要不变性,但对这样一个字符串的引用在其整个生命周期内始终等于"abc"(这确实需要不变性),这一事实使得在保持与以前的值相等至关重要的情况下,可以将其用作键,从而更容易确保正确性(字符串确实常用作键).
  6. 从概念上讲,不变会更有意义.如果我们在圣诞节上加一个月,我们没有改变圣诞节,我们在一月下旬产生了一个新的日期.因此,Christmas.AddMonths(1)产生一个新的DateTime而不是改变一个可变的DateTime是有意义的.(另一个示例,如果我作为一个可变对象更改了我的名称,那么更改的是我使用的名称,"Jon"保持不变,而其他Jon不会受到影响.
  7. 复制既快速又简单,只需复制return this个克隆.由于副本无论如何都无法更改,所以假装某个东西是它自己的副本是安全的.
  8. [编辑,我忘了这个].内部状态可以在对象之间安全地共享.例如,如果要实现由数组、起始索引和计数支持的列表,那么创建子范围最昂贵的部分就是复制对象.然而,如果它是不可变的,那么子范围对象可以引用同一个数组,只需更改开始索引和计数,构建时间会有相当大的变化.

总的来说,对于那些目的中没有经历变化的对象来说,保持不变有很多好处.主要的缺点是需要额外的构造,尽管即使在这里也常常被夸大(请记住,在StringBuilder变得比具有其固有构造的等效串联序列更高效之前,您必须进行几个附加).

如果可变性是一个对象(希望由一个工资永远不会改变的雇员对象建模)的目的的一部分,这将是一个缺点,尽管有时即使这样,它也会很有用(在许多web和其他无状态应用程序中,执行读取操作的代码与执行更新的代码是分开的,使用不同的对象可能是自然的——我不会让一个对象不可变,然后强制使用该模式,但如果我已经有了该模式,我可能会让我的"读取"对象不可变,以获得性能和正确性保证增益).

抄写是一种中间立场.这里的"real"类包含对"state"类的引用.状态类在复制操作中共享,但如果更改状态,则会创建状态类的新副本.这比C++更常用于C++,这就是为什么它是STD:String拥有一些但不是全部的不变类型的优点,同时保持不变.

.net相关问答推荐

如何处理以用户为中心的CSP现时值?(uc-lock.bundle.js)

为什么.Net 8.0.100是预览版?

无法解析此引用.找不到程序集

C# 中的批量更新

如何创建 LINQ to SQL 事务?

为什么 Any() 不适用于 c# null 对象

如何从 appsettings.json 中获取价值

读取方法的属性值

如何授予所有用户对我的应用程序创建的文件的完全权限?

.NET 的 String.Normalize 有什么作用?

如何在 C# 中将 null 值设置为 int?

当我们按下 Enter 键时启动的 WPF 文本框命令

String.Replace() 与 StringBuilder.Replace()

等效于 C# 在 powershell 中的使用关键字?

如何找到二维数组的大小?

找不到库 hostpolicy.dll

Guid.Parse() 或 new Guid() - 有什么区别?

具有不同身份验证标头的 HttpClient 单个实例

记录器包装器最佳实践

使用 C# vs F# 或 F# vs C# 有什么好处?