字符串连接是否在内部使用StringBuilder?

Jam*_*ead 12 c# compiler-construction string concatenation

我的三个同事告诉我,没有理由使用StringBuilder来代替使用+运算符的连接.换句话说,这对一堆字符串很好:myString1 + myString2 + myString3 + myString4 + mySt...

他们使用的基本原理是,从.NET 2开始,如果使用+运算符,C#编译器将构建相同的IL,就像使用StringBuilder一样.

这对我来说是新闻.他们是对的吗?

Jef*_*tes 25

不,他们不正确.字符串连接创建一个新的,stringStringBuilder使用一个可变大小的缓冲区来构建字符串,只在string调用时创建一个对象ToString().

如果您想进一步阅读有关该主题的内容,那么在互联网上有很多关于字符串连接技术的讨论.大多数注意力集中在循环中使用时不同方法的效率.在这种情况下,StringBuilder使用字符串运算符进行字符串连接比10个或更多字符串的连接更快,这应该表明它必须使用与连接不同的方法.

也就是说,如果你连接常量字符串值,字符串运算符会更好,因为编译器会将它们分解,如果执行非循环连接,使用运算符会更好,因为它们应该导致单个调用string.Concat.

  • 字符串连接**不会为每个`+`操作创建一个新字符串:`a + b + c + d`被转换为`string.Concat(a,b,c,d)` (9认同)
  • @Doc,那不是OP要求的。 (2认同)

Dar*_*rov 16

不,他们不正确,它不会产生相同的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;
}
Run Code Online (Sandbox Code Playgroud)

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 
}
Run Code Online (Sandbox Code Playgroud)

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 
}
Run Code Online (Sandbox Code Playgroud)

你也可能会觉得这篇文章很有趣.

  • String.Concat预先知道要连接的字符串的长度,因此与StringBuilder不同,它可以立即分配一个具有正确大小的新字符串,并且不需要分配增长缓冲区并修剪结果. (6认同)

Tho*_*rin 5

不,他们不是.他们肯定产生不同的IL.它使用不同的调用:String.Concat在非StringBuilder情况下.

String.Concat调用一个名为的私有方法ConcatArray,它只分配一个新字符串,足以保存最终结果.所以,非常不同,但这并不意味着使用+运算符连接的效率低于使用StringBuilder.事实上,它几乎肯定更有效率.此外,在连接常量的情况下,它在编译时完成.

但是,当您在循环中进行连接时,编译器无法执行此类优化.在这种情况下,StringBuilder对于相当长的字符串使用会更好.