2009-07-02 13 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?