StringBuilder如何在C#内部工作?

StringBuilder如何工作?

它在内部做什么? 它使用不安全的代码吗? 为什么它如此之快(与+运算符相比)?

使用+运算符构建字符串时:

 string s = "01"; s += "02"; s += "03"; s += "04"; 

然后在第一个连接中,我们创建一个长度为4的新字符串并将“01”和“02”复制到其中 – 复制了四个字符。 在第二个连接中,我们创建一个长度为6的新字符串并将“0102”和“03”复制到其中 – 复制了六个字符。 在第三个concat上,我们创建一个长度为8的字符串并将“010203”和“04”复制到其中 – 复制了8个字符。 到目前为止,这个八个字符的字符串总共复制了4 + 6 + 8 = 18个字符。 继续。

 ... s += "99"; 

在第98次结束时,我们制作一个长度为198的字符串,并将“010203 … 98”和“99”复制到其中。 这给了我们总共4 + 6 + 8 + … + 198 =很多,为了使这个198字符串。

字符串构建器不会完成所有复制。 相反,它维护一个希望比最终字符串更大的可变数组,并根据需要将新内容填充到数组中。

当猜测错误并且数组变满时会发生什么? 有两种策略。 在该框架的先前版本中,字符串构建器在数组已满时重新分配并复制该数组,并将其大小加倍。 在新实现中,字符串构建器维护一个相对较小的数组的链接列表,并在旧的数组变满时将新数组附加到列表的末尾。

此外,正如您所推测的那样,字符串构建器可以使用“不安全”代码来提高其性能。 例如,将新数据写入数组的代码已经检查过数组写入是否在边界内。 通过关闭安全系统,它可以避免每次写入检查抖动可能会插入以validation每次写入arrays是否安全。 字符串生成器执行许多这样的操作来执行诸如确保重用缓冲区而不是重新分配缓冲区,确保避免不必要的安全检查等等。 除非你真的善于正确地编写不安全的代码,否则我建议不要使用这些恶作剧,而且确实需要摒弃每一个性能。

我相信StringBuilder的实现在版本之间已经发生了变化。 从根本上说,它保持了某种forms的可变结构。 我相信它曾经使用过一个仍在变异的字符串(使用内部方法),并确保它在返回后永远不会发生变异。

StringBuilder在循环中使用字符串连接更快的原因恰恰是因为它的可变性 – 它不需要在每次突变后构造一个新字符串,这意味着要复制字符串中的所有数据等。

对于单个连接,使用+实际上比使用StringBuilder更有效。 只有当您执行多个操作并且您并不真正需要StringBuilder闪耀的中间结果时。

有关更多信息,请参阅我在StringBuilder文章 。

Microsoft CLR确实使用内部调用执行某些操作(与不安全的代码不完全相同)。 与一串+串联字符串相比,最大的性能优势是它写入char[]并且不会创建尽可能多的中间字符串。 当您调用ToString()时,它会根据您的内容构建一个完整的,不可变的字符串。

与不能使用的常规String相比, StringBuilder使用可以更改的字符串缓冲区。 当你调用StringBuilderToString方法时,它只会冻结字符串缓冲区并将其转换为常规字符串,因此它不必再多次复制所有数据。

由于StringBuilder可以更改字符串缓冲区,因此不必为字符串数据的每次更改创建新的字符串值。 当您使用+运算符时,编译器将其转换为String.Concat调用,该调用将创建一个新的字符串对象。 这段看似无辜的代码:

 str += ","; 

编译成这个:

 str = String.Concat(str, ",");