2009-07-02 14 views
3

我读过StringBuilder类型有一个限制(默认值是16个字符),并且当你向它附加一些文本时,超出了它的限制,创建一个新的实例,其上限并将数据复制到它。我想,用下面的代码:System.Text.StringBuilder限制

StringBuilder test = new StringBuilder("ABCDEFGHIJKLMNOP",16); 
test.Append("ABC"); 

而且CIL的生成是:

.maxstack 3 
    .locals init (class [mscorlib]System.Text.StringBuilder V_0) 
    IL_0000: nop 
    IL_0001: ldstr  "ABCDEFGHIJKLMNOP" 
    IL_0006: ldc.i4.s 16 
    IL_0008: newobj  instance void [mscorlib]System.Text.StringBuilder::.ctor(string, int32) 
    IL_000d: stloc.0 
    IL_000e: ldloc.0 
    IL_000f: ldstr  "ABC" 
    IL_0014: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) 
    IL_0019: pop 
    IL_001a: ret 

设置限制,比方说,32:

StringBuilder test = new StringBuilder("ABCDEFGHIJKLMNOP",32); 
test.Append("ABC"); 

生成完全相同IL代码。 我期望的是在第一种情况下创建一个新实例,并在第二种情况下更改实例的值,这显然没有发生,任何线索为什么?

回答

8

所有有趣的事情发生在这一行:

IL_0014: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) 

这是你拨打电话到Append()方法,但你已经张贴了IL不包含方法的主体。查看source code中的StringBuilder类(它是在允许您查看的许可证下发布的),并查看Append()方法中发生的情况。

剧透警告!查看Append()的源代码将会发现,只要连接字符串的长度超出缓冲区的当前大小,内部缓冲区的确会增加。

+0

但是生成的CIL代码表明它不是,据我所知,CIL代码每当我向文件添加文本时都会创建一个新的字符串生成器,就像在处理字符串时创建新字符串一样。 – 2009-07-02 13:23:45

+0

不确定你的意思 - 在IL中你在找什么?我只看到一个对构造函数(IL_0008)的调用,另一个对Append()方法(IL_0008)。这似乎很好地反映了你的C#代码。 – 2009-07-02 13:27:52

+1

不,不会创建新的stringbuilder对象。现有的stringbuilder分配一个新的BUFFER。 – 2009-07-02 13:39:21

2

与大多数Collection类一样,StringBuilder具有Capacity属性。当集合增长超过容量时,内部数据结构(不是对象本身)被重新分配,我认为大多数(我确信StringBuilder和List <>)都使用加倍策略。是的,这涉及到创建一个新的数组并复制旧数据。

如果您有关于endresult有多大将是任何信息,使用类似:

var sb = new StringBuilder(n); // set initial Capacity=n 

这是不是一个坏主意,采取较高的估计,也许添加一些额外的空间。更好地分配几个字符太多,然后中途复制。

这是我们支付的具有可以自动增长的集合的价格(StringBuilder就像是一个char集合)。我想,替代方案,如块链表,被认为是复杂的。

1

我认为你误读了IL代码。以下行:

IL_0014: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) 

......并不意味着一个新的StringBuilder实例被创建;它只是对Append方法的调用。

如果调用Append会导致一个字符串,它比StringBuilder的电流容量越长,它会创建一个新的String实例内部;它仍然是相同的StringBuilder实例。

3

这个C#代码

using System.Text; 

internal class Program 
{ 
    internal static void Main(string[] args) 
    { 
     StringBuilder test = new StringBuilder("ABCDEFGHIJKLMNOP", 16); 
     test.Append("ABC"); 

     StringBuilder test2 = new StringBuilder("ABCDEFGHIJKLMNOP", 32); 
     test2.Append("ABC"); 
    } 
} 

产生以下IL(根据反射镜):

.class private auto ansi beforefieldinit Program 
    extends [mscorlib]System.Object 
{ 
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed 
    { 
     .maxstack 8 
     L_0000: ldarg.0 
     L_0001: call instance void [mscorlib]System.Object::.ctor() 
     L_0006: ret 
    } 

    .method assembly hidebysig static void Main(string[] args) cil managed 
    { 
     .entrypoint 
     .maxstack 3 
     .locals init (
      [0] class [mscorlib]System.Text.StringBuilder test, 
      [1] class [mscorlib]System.Text.StringBuilder test2) 
     L_0000: nop 
     L_0001: ldstr "ABCDEFGHIJKLMNOP" 
     L_0006: ldc.i4.s 0x10 
     L_0008: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(string, int32) 
     L_000d: stloc.0 
     L_000e: ldloc.0 
     L_000f: ldstr "ABC" 
     L_0014: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) 
     L_0019: pop 
     L_001a: ldstr "ABCDEFGHIJKLMNOP" 
     L_001f: ldc.i4.s 0x20 
     L_0021: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(string, int32) 
     L_0026: stloc.1 
     L_0027: ldloc.1 
     L_0028: ldstr "ABC" 
     L_002d: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) 
     L_0032: pop 
     L_0033: ret 
    } 
} 

所以在这里,0x100x20被用于初始化testtest2,这意味着你可能看在你的测试中错误的IL?