簡體   English   中英

字符串連接是否在內部使用StringBuilder?

[英]Does string concatenation use StringBuilder internally?

我的三個同事告訴我,沒有理由使用StringBuilder代替使用+運算符進行連接。 換句話說,這對一堆字符串很好: myString1 + myString2 + myString3 + myString4 + mySt...

他們使用的基本原理是,從.NET 2開始,如果使用+運算符,C#編譯器將構建相同的IL,就像使用StringBuilder一樣。

這對我來說是新聞。 他們是對的嗎?

不,他們不正確。 字符串連接創建一個新stringStringBuilder使用可變大小的緩沖區來構建字符串,只在調用ToString()時創建一個string對象。

如果您想進一步閱讀有關該主題的內容,那么在互聯網上有很多關於字符串連接技術的討論。 大多數注重在循環中使用時不同方法的效率。 在這種情況下, StringBuilder使用字符串運算符進行字符串連接比10個或更多字符串的連接更快,這應該表明它必須使用與連接不同的方法。

也就是說,如果你連接常量字符串值,字符串運算符會更好,因為編譯器會將它們分解,如果執行非循環連接,使用運算符會更好,因為它們應該導致單個調用string.Concat

不,他們不正確,它不會產生相同的IL:

static string StringBuilder()
{
    var s1 = "s1";
    var s2 = "s2";
    var s3 = "s3";
    var s4 = "s4";
    var sb = new StringBuilder();
    sb.Append(s1).Append(s2).Append(s3).Append(s4);
    return sb.ToString();
}

static string Concat()
{
    var s1 = "s1";
    var s2 = "s2";
    var s3 = "s3";
    var s4 = "s4";
    return s1 + s2 + s3 + s4;
}

IL的StringBuilder:

.method private hidebysig static string StringBuilder() cil managed
{
    .maxstack 2
    .locals init (
        [0] string s1,
        [1] string s2,
        [2] string s3,
        [3] string s4,
        [4] class [mscorlib]System.Text.StringBuilder sb)
    L_0000: ldstr "s1"
    L_0005: stloc.0 
    L_0006: ldstr "s2"
    L_000b: stloc.1 
    L_000c: ldstr "s3"
    L_0011: stloc.2 
    L_0012: ldstr "s4"
    L_0017: stloc.3 
    L_0018: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_001d: stloc.s sb
    L_001f: ldloc.s sb
    L_0021: ldloc.0 
    L_0022: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0027: ldloc.1 
    L_0028: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_002d: ldloc.2 
    L_002e: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0033: ldloc.3 
    L_0034: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0039: pop 
    L_003a: ldloc.s sb
    L_003c: callvirt instance string [mscorlib]System.Object::ToString()
    L_0041: ret 
}

IL of Concat:

.method private hidebysig static string Concat() cil managed
{
    .maxstack 4
    .locals init (
        [0] string s1,
        [1] string s2,
        [2] string s3,
        [3] string s4)
    L_0000: ldstr "s1"
    L_0005: stloc.0 
    L_0006: ldstr "s2"
    L_000b: stloc.1 
    L_000c: ldstr "s3"
    L_0011: stloc.2 
    L_0012: ldstr "s4"
    L_0017: stloc.3 
    L_0018: ldloc.0 
    L_0019: ldloc.1 
    L_001a: ldloc.2 
    L_001b: ldloc.3 
    L_001c: call string [mscorlib]System.String::Concat(string, string, string, string)
    L_0021: ret 
}

你也可能會覺得這篇文章很有趣。

不,他們不是。 他們肯定產生不同的IL。 它使用不同的調用:非StringBuilder情況下的String.Concat

String.Concat調用一個名為ConcatArray的私有方法,該方法分配一個新字符串,其長度足以保存最終結果。 所以,非常不同,但這並不意味着使用+運算符連接的效率低於使用StringBuilder。 事實上,它幾乎肯定更有效率。 此外,在連接常量的情況下,它在編譯時完成。

但是,當您在循環中進行連接時,編譯器無法執行此類優化。 在這種情況下,使用StringBuilder對於相當長的字符串會更好。

答案是它取決於你如何連接。 如果你使用帶有靜態字符串的+運算符,那么你的朋友是正確的 - 不需要字符串構建器。 但是,如果使用字符串變量或+ =運算符,則需要重新分配字符串。

真正了解這里發生了什么的方法是編寫一些代碼然后反編譯。

讓我們構建一些測試代碼並使用IL視圖在Reflector中查看它(或者您可以使用ILDASM,無論您喜歡哪個

首先,一個基線 - 這個方法根本沒有連接:


static void NoConcat()
{
  string test = "Hello World";
}

現在這里是IL:


.method private hidebysig static void NoConcat() cil managed
{
    .maxstack 1
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello World"  <----------NO reallocation!
    L_0006: stloc.0 
    L_0007: ret 
}

好吧,沒有驚喜,對吧?

現在讓我們看一些肯定會重新分配字符串的代碼,所以我們知道它是什么樣的:


static void Concat2()
{
  string test = "Hello";
  test += " ";
  test += "World";
}

這是IL,請注意重新分配(它調用string.Concat,這會導致分配一個新字符串):


.method private hidebysig static void Concat2() cil managed
{
    .maxstack 2
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello"
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldstr " "
    L_000d: call string [mscorlib]System.String::Concat(string, string)
    L_0012: stloc.0 
    L_0013: ldloc.0 
    L_0014: ldstr "World"
    L_0019: call string [mscorlib]System.String::Concat(string, string)
    L_001e: stloc.0 
    L_001f: ret 
}

好吧,現在如何不會導致重新分配的串聯 - 我們將使用“+”運算符連接靜態字符串:


static void Concat1()
{
  string test = "Hello" + " " + "World";
}

這是IL - 看看編譯器有多聰明! 它不使用concat - 它與第一個例子相同:


.method private hidebysig static void Concat1() cil managed
{
    .maxstack 1
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello World"
    L_0006: stloc.0 
    L_0007: ret 
}

現在讓我們玩得開心吧。 如果我們混合靜態字符串和變量怎么辦? (這是你使用stringbuilder可能還會更好的地方)


static void Concat3(string text)
{
  string test = "Hello" + " " + text + " World";
}

和IL。 請注意,將“Hello”和“”組合為常量非常智能,但仍需要為文本變量執行concat:


.method private hidebysig static void Concat3(string text) cil managed
{
    .maxstack 3
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello "
    L_0006: ldarg.0 
    L_0007: ldstr " World"
    L_000c: call string [mscorlib]System.String::Concat(string, string, string)
    L_0011: stloc.0 
    L_0012: ret 
}

我通常遵循以下規則:

  1. 如果子字符串的數量是預先知道的,請使用連接。 這涵蓋了像str1 + str2 + str3 + ......這樣的情況,無論它們有多少。

  2. 如果子字符串已在數組中,請使用string.join

  3. 如果在循環中構建字符串,請使用StringBuilder

String和StringBuilder之間略有不同:

連接String將創建一個新的字符串對象,它是串聯的結果。 連接StringBuilder會修改字符串對象。

所以他們不正確。

字符串連接和StringBuidler之間存在巨大的性能差異。 我們的網絡服務太慢了。 我們將所有的串貓改為StringBuilder.Appends並且速度更快了!

不,字符串連接在內部不使用StringBuilder。 但是,在您的特定示例中,使用StringBuilder沒有任何優勢。

這適用於幾個字符串(您只創建一個新字符串):

myString = myString + myString2 + myString3 + myString4 + mySt...

這不是(你正在創建和分配4個字符串等):

myString = myString + myString2;
myString = myString + myString3;
myString = myString + myString4;
myString = myString + myString5;

在關於此問題的所有stackoverflow問題中,這有一個最好的答案: String vs. StringBuilder

尋找兩個答案,一個是Jay Bazuzi,另一個是James Curran。

此外,強烈建議,Jeff Atwood使用實際測試來比較字符串連接/構建的這些和其他場景,例如: http//www.codinghorror.com/blog/2009/01/the-sad-tragedy-of-micro-優化-theater.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM