StringBuilder扩容是否创建新实例?与书籍描述的冲突疑问
关于StringBuilder扩容是否创建新实例的疑问
我写了一段测试代码,查看其IL代码后发现只有一行newobj指令,也就是只创建了一个StringBuilder实例。但《Pro C#10 with .Net 6》第89页提到:
If you append more characters than the specified limit, the StringBuilder object will copy its data into a new instance and grow the buffer by the specified limit.
(中文译:当添加的字符超过指定限制时,StringBuilder对象会将其数据复制到新实例中,并按指定限制扩容缓冲区)
这让我产生疑问:我的理解是不是有错误?
测试代码
StringBuilder sb = new StringBuilder("test", 4); sb.Append('\n'); sb.AppendLine("test1"); sb.AppendLine("test2"); sb.AppendLine("test3"); sb.AppendLine("test4");
对应的IL代码
// [3 1 - 3 49] IL_0000: ldstr "test" IL_0005: ldc.i4.4 IL_0006: newobj instance void [System.Runtime]System.Text.StringBuilder::.ctor(string, int32) IL_000b: stloc.0 // sb // [4 1 - 4 17] IL_000c: ldloc.0 // sb IL_000d: ldc.i4.s 10 // 0x0a IL_000f: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(char) IL_0014: pop // [5 1 - 5 24] IL_0015: ldloc.0 // sb IL_0016: ldstr "test1" IL_001b: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string) IL_0020: pop // [6 1 - 6 24] IL_0021: ldloc.0 // sb IL_0022: ldstr "test2" IL_0027: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string) IL_002c: pop // [7 1 - 7 24] IL_002d: ldloc.0 // sb IL_002e: ldstr "test3" IL_0033: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string) IL_0038: pop // [8 1 - 8 24] IL_0039: ldloc.0 // sb IL_003a: ldstr "test4" IL_003f: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string) IL_0044: pop IL_0045: ret
解答
你没有理解错,但书中的描述存在用词不准确的问题——StringBuilder扩容时并不会创建新的实例对象,而是会重新分配内部的字符缓冲区数组,把原有数据复制到新的缓冲区里,实例本身还是同一个。
你看到的IL代码里只有一个newobj指令,完全符合实际运行逻辑:
- 你创建的StringBuilder初始容量是4,初始字符串"test"刚好占满4个字符。
- 后续的Append和AppendLine操作会触发扩容,但扩容仅针对内部的char数组,不会创建新的StringBuilder对象,所以IL里不会出现新的
newobj指令。
书中的描述可能是把“新的缓冲区数组”误表述成了“新实例”。实际查看.NET源码就能确认:StringBuilder的扩容逻辑是重新分配更大的内部数组,复制数据后替换原有数组,整个过程中实例引用始终不变。
内容的提问来源于stack exchange,提问作者Humble Newbie
相关产品推荐
相关产品推荐

