Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
StringBuilder在C#中如何在内部工作?_C#_.net_String_Stringbuilder - Fatal编程技术网

StringBuilder在C#中如何在内部工作?

StringBuilder在C#中如何在内部工作?,c#,.net,string,stringbuilder,C#,.net,String,Stringbuilder,StringBuilder是如何工作的 它在内部做什么?它使用不安全的代码吗? 为什么它这么快(与+操作员相比)?Microsoft CLR使用内部调用执行一些操作(与不安全代码不同)。与大量的+串联字符串相比,最大的性能优势在于它写入字符[],并且不会创建那么多的中间字符串。当您调用ToString()时,它会根据您的内容构建一个完整的、不可变的字符串。StringBuilder的实现在不同版本之间发生了变化。但从根本上说,它保持了某种形式的可变结构。我相信它曾经使用一个仍在变异的字符串(使用

StringBuilder
是如何工作的

它在内部做什么?它使用不安全的代码吗?
为什么它这么快(与
+
操作员相比)?

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

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

之所以
StringBuilder
比在循环中使用字符串串联更快,正是因为它的易变性——它不需要在每次变异后构造新字符串,这意味着复制字符串中的所有数据等

对于单个串联,实际上使用
+
比使用
StringBuilder
更有效。只有当您执行多个操作时,才真正需要
StringBuilder
所显示的中间结果


有关更多信息,请参阅。

与无法更改的常规
字符串相比,
StringBuilder
使用可以更改的字符串缓冲区。当您调用
StringBuilder
ToString
方法时,它将冻结字符串缓冲区并将其转换为常规字符串,因此它不需要额外复制所有数据

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

str += ",";
汇编成:

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

使用+运算符生成字符串时:

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

...
s += "99";
在第98节,我们制作了一个长度为198的字符串,并将“010203…98”和“99”复制到其中。这给了我们总共4+6+8+…+198=很多,以便生成此198个字符串

字符串生成器不会进行所有的复制。相反,它维护一个希望大于最终字符串的可变数组,并根据需要向数组中填充新内容

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


此外,正如您所猜测的,字符串生成器可以使用“不安全”代码来提高其性能。例如,将新数据写入数组的代码可能已经检查了数组写入是否在范围内。通过关闭安全系统,它可以避免抖动可能插入的每次写入检查,以验证对阵列的每次写入都是安全的。stringbuilder使用了很多这样的技巧来做一些事情,比如确保缓冲区被重用而不是重新分配,确保避免不必要的安全检查,等等。我建议您不要使用这些诡计,除非您真的擅长正确编写不安全代码,并且确实需要弥补每一点性能。

找到了您大约6年前对这个主题的评论:您介意提供更多细节吗?当您将项目分组并创建一个巨大的字符串时,是否会重新定义数组大小?当调用tostring时,它是否只是一个指向char数组(或链表)的指针数组,该数组被转换为单个对象?您介意引用一个源代码吗?内部代码是透明的,但由于它有类似StringBuilder.EnsureCapacity的方法,因此它让人相信它是一个大的缓冲区,在必要时会增长。这不是比使用链表并在最后合并它效率低吗?我的意思是,如果你有一个一兆字符串,你是附加?你必须为已经存在的东西创建一个副本,这需要时间和资源。如果你只是做了一个指向原始的指针,你就不必担心它会改变,因为它是不可变的,gac也不会因为你引用了不可变的字符串而点击它来删除它。@JSWork,如果我说
stringBuilder.Remove(10232000)
,会怎么样。如果你有一个字符串的链表,这是一个复杂的算法。我敢肯定,这样做效率不高。但是,如果您知道不需要插入、删除、替换等功能,@JSWork,
new StringBuilder(2048*1024)
您可以指定足够大的初始容量,这将最大限度地降低重新确定尺寸的成本,那么可以通过这种方式实现您自己的LLStringBuilder类。(你可能已经知道了,但这可能会让未来的读者受益。)如果你对细节感到好奇,你也可以直接看一下。在
refsrc目录下有一个带注释的
StringBuilder.cs
副本