我的三位同事告诉我,在使用 +
运算符拼接字符串时,没有必要使用 StringBuilder。换句话说,这样做是可以的:myString1 + myString2 + myString3 + myString4 + mySt...
他们使用的理由是自 .NET 2 以来,C# 编译器将生成与使用 StringBuilder 相同的 IL。
这对我来说是新闻。他们正确吗?
我的三位同事告诉我,在使用 +
运算符拼接字符串时,没有必要使用 StringBuilder。换句话说,这样做是可以的:myString1 + myString2 + myString3 + myString4 + mySt...
他们使用的理由是自 .NET 2 以来,C# 编译器将生成与使用 StringBuilder 相同的 IL。
这对我来说是新闻。他们正确吗?
string
对象,而StringBuilder
使用可变大小的缓冲区来构建字符串,在调用ToString()
时才会创建一个string
对象。StringBuilder
比字符串连接更快,这表明它必须使用不同的方法进行连接。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;
}
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
}
Concat的IL代码:
.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
}
你可能会发现这篇文章很有趣。
String.Concat
在内部做了什么?它是否使用了 StringBuilder
?如果是这样,那么调用一个使用 StringBuilder
并返回字符串的函数与使用单个 Concat 调用并返回字符串的调用没有区别。当开始进行 多个 调用时,差异就会出现。或者我错了吗? - Anthony PegramString.Concat
。
String.Concat
调用一个名为ConcatArray
的私有方法,该方法分配一个新字符串,其长度恰好足以容纳最终结果。因此,这两种方法非常不同,但这并不意味着使用+运算符连接字符串比使用StringBuilder效率低。事实上,前者几乎肯定更高效。此外,在常量连接的情况下,连接操作是在编译时完成的。StringBuilder
会更好。
.方法 私有 hidebysig 静态 空的 Concat1() cil 管理
{
.最大堆栈 1
.本地变量 初始化 (
[0] 字符串 测试)
L_0000: nop
L_0001: ldstr "你好世界"
L_0006: stloc.0
L_0007: ret
}
现在让我们来玩一点。 如果我们混合使用静态字符串和变量会怎样?(这时您可能最好使用stringbuilder)
静态 void Concat3(string text)
{
string test = "Hello" + " " + text + " World";
}
IL如下所示。 请注意,它足够聪明,将“Hello”和“ ”组合为一个常数,但仍必须为文本变量进行连接:
.方法 私有 hidebysig 静态 空的 Concat3(string text) cil 管理
{
.最大堆栈 3
.本地变量 初始化 (
[0] 字符串 测试)
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
}
我通常遵循以下规则:
如果子字符串的数量是已知的,请使用连接。这适用于像str1 + str2 + str3 + ...这样的情况,无论它们有多少个。
如果子字符串已经在数组中,请使用string.join
如果在循环中构建字符串,请使用StringBuilder
String.Concat
替代 String.Join
:) - ThorarinString.Join
是所有.NET字符串组合函数中最高效的,您可以在谷歌上查找基准测试结果来证明这一点。然而,通常情况下,string.Join
并不易于使用。 - Chris MarisicString.Concat
实现中就出现了一些史诗级别的失败。 - ThorarinmyString = 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-optimization-theater.html
String和StringBuilder之间存在一些细微的差别:
连接一个String将创建一个新的字符串对象,该对象是连接的结果。连接一个StringBuilder会修改字符串对象。
因此它们并不正确。
字符串拼接和StringBuilder之间有巨大的性能差异。我们有一个速度太慢的Web服务。我们将所有的字符串拼接改为StringBuilder.Append,速度就快了很多!
+
操作创建一个新的字符串:将a + b + c + d
转换为string.Concat(a, b, c, d)
。 - dtbfor(i=0;i<n;++i) s = s +a[i];
这样的循环,它就会执行。 - Doc Brown