字符串 (String)所有主题 (All Topics)架构师 (Architect)高级 (Advanced)初学者 (Beginner)中级 (Intermediate)开发 (Dev).NETC#
字符串来自地球,而 StringBuilder 来自火星。






4.95/5 (38投票s)
在这里,我们将讨论 .NET 中的字符串(Strings)和 StringBuffer。
引言
我曾经幸福地和字符串“结婚”很长时间,直到我了解到“字符串是不可变的”这个事实,并且不适合所有场景。最近,我正在开发一个重型的 HTML 解析器应用程序,并且程序经常出现内存溢出。完整的 HTML 解析逻辑都使用了字符串变量。
经过研究,我了解到主要原因是字符串的不可变特性。不可变意味着一旦分配数据就无法更改。例如,如果您像下面的代码一样使用字符串变量进行循环。每次赋值给字符串都会创建变量的新副本,并且将先前的副本发送到垃圾回收。因此,下面的 for 循环会生成不同的数据内存副本,最近创建的是当前值。

现在你一定想知道为什么会有这种荒谬的行为。任何像我一样的普通人都可以得出结论,这效率不高,也不符合逻辑。
为线程安全做出的牺牲
在开始讨论解决方案之前,我想了解为什么 Microsoft 团队会想到这种奇怪的行为。感谢 http://stackoverflow.com/questions/2365272/why-net-string-is-immutable ,事情开始变得合乎逻辑。如果您在多线程场景中使用字符串变量,则每个线程修改都会创建新的内存副本,从而确保您不会遇到多线程问题。换句话说,当创建新的数据副本时,线程安全性是内置的。
并非所有工作都在船上完成
接下来让我感到困扰的是,如果我的应用程序不是多线程的怎么办?如果我的主要目的是节省内存资源并确保我不会出现内存溢出问题怎么办?这就是来自火星的英雄“StringBuilder”的用武之地。“StringBuilder”是可变的,换句话说,如果您更改变量数据,则会修改相同的内存位置。哇,与字符串相比,这看起来可以在大量的连接操作期间节省大量内存。

我想亲眼看看地球是否是平的
作为一个好奇的开发人员,我很难理解字符串会在内部创建不同的数据副本。出于好奇,我下载了 CLR Profiler 并运行了两个代码测试,如下所示。一个是用于字符串,如下所示。
string x =""; for (inti = 0; i< 10000; i++) { x = "Shiv"+ x; }

一个是用于字符串构建器。
StringBuilder x = newStringBuilder(); for (inti = 0; i< 10000; i++) { x.Append("Shiv"); }

注意分配的字节数,400235631 字节远大于 136597 字节。
观看下面的视频以了解实际演示
如果您不相信我写的内容,请观看以下实际视频演示
进一步阅读,请观看下面的面试准备视频和分步视频系列。