2011-01-21 53 views
6
A = string.Concat("abc","def") 

B = "abc" + "def" 

A对乙在c#中添加字符串,编译器如何执行它?

最近我一直在困惑,为什么很多人会说,相比B.绝对是做了更快的处理,但是,事情是,他们只会说,因为有人这么说,或者因为它就是这样。我想我可以从这里听到更好的解释。

编译器如何处理这些字符串?

谢谢!

+2

对于这种大小的字符串,无关紧要 – 2011-01-21 10:44:49

回答

12

我加入C#编译器团队时做的第一件事是我重写了字符串连接的优化器。美好时光。

如前所述,常量字符串的字符串concats是在编译时完成。非常量字符串做一些花哨的东西:

a + b --> String.Concat(a, b) 
a + b + c --> String.Concat(a, b, c) 
a + b + c + d --> String.Concat(a, b, c, d) 
a + b + c + d + e --> String.Concat(new String[] { a, b, c, d, e }) 

这些优化的好处是,该String.Concat方法可以看看所有的参数,确定它们的长度的总和,然后做出一个大的字符串,可以掌握所有结果。

这里有一个有趣的一个。假设你有一个返回字符串的方法M:

s = M() + ""; 

如果M()返回null,那么结果是空字符串。 (null + empty是空的。)如果M不返回null,那么结果不会因空字符串的串联而改变。因此,这实际上是优化的,因为根本不是对String.Concat的调用!它变成

s = M() ?? "" 

整洁,呃?

5

在C#中,字符串的加法运算符只是String.Concat的语法糖。您可以通过在反射器中打开输出组件来验证。

需要注意的另一件事是,如果您的代码中包含字符串文字(或常量)(如示例中所示),编译器甚至将其更改为B = "abcdef"

但是,如果使用String.Concat带有两个字符串文字或常量,String.Concat仍然会被调用,跳过优化,因此+操作实际上会更快。

所以,总结一下:

stringA + stringB变得String.Concat(stringA, stringB)
"abc" + "def"成为"abcdef
String.Concat("abc", "def")保持不变

别的东西,我刚做了尝试:

在C++/CLI,"abc" + "def" + "ghi” 实际上是翻译成String.Concat(String.Concat("abc", "def"), "ghi")

+3

不与两个字符串文字不同:`B`将直接设置为“abcdef”。 – LukeH 2011-01-21 10:45:50

+0

在发布后立即添加:) – Botz3000 2011-01-21 10:48:17

1

事实上,B的期间解析编译时间。你将以B = "abcdef"结束,而对于A,连接被推迟到执行时间。

+1

要添加到此,* not *面对文字时使用`+`将被转换为`string.Concat()` – Joey 2011-01-21 10:47:34

1

如果字符串文字,在你的问题,然后分配给B的字符串的连接将在编译时完成。你的榜样转化为:

string a = string.Concat("abc", "def"); 
string b = "abcdef"; 

如果字符串不是字面值,则编译器会将+操作转化为Concat电话。

所以这...

string x = GetStringFromSomewhere(); 
string y = GetAnotherString(); 

string a = string.Concat(x, y); 
string b = x + y; 

...被翻译成这个在编译时:

string x = GetStringFromSomewhere(); 
string y = GetAnotherString(); 

string a = string.Concat(x, y); 
string b = string.Concat(x, y); 
1

在这种特殊情况下,两者实际上是相同的。编译器会将第二个变体(使用+运算符的变体)转换为第一个变体Concat的调用。

那么,如果两个实际包含的字符串变量被连接在一起。

此代码:

B = "abc" + "def"; 

实际上转变成这样,无连接的所有:

B = "abcdef"; 

这是可以做到,因为加法的结果可以在编译时计算,所以编译器会这样做。

但是,如果你使用的是这样的:

A = String.Concat(stringVariable1, stringVariable2); 
B = stringVariable1 + stringVariable2; 

然后他们两个会产生相同的代码。

但是,我想知道那些“多”的说法,因为我认为它有些不同。

我认为他们说的是字符串连接不好,你应该使用StringBuilder或类似的。

举例来说,如果你这样做:

String s = "test"; 
for (int index = 1; index <= 10000; index++) 
    s = s + "test"; 

那么会发生什么情况是,通过循环每次迭代中,你将建立一个新的字符串,而让旧有资格进行垃圾回收。

此外,每个这样的新字符串都会复制旧字符串的所有内容,这意味着您将移动大量内存。

而下面的代码:

StringBuilder sb = new StringBuilder("test"); 
for (int index = 1; index <= 10000; index++) 
    sb.Append("test"); 

将改为使用内部缓冲区,比什么需要更大一些,以防万一你需要更多的文字追加到它。当该缓冲区变满时,将分配一个更大的新缓冲区,并将旧的剩下的一个留给垃圾收集。

所以在使用内存和CPU使用率方面,后来变种好得多。

除此之外,我会尽力避免过分关注“代码变量X比Y更好”,超出了你已有的经验。举例来说,我使用StringBuilder现在只是因为我知道的情况,但这并不是说所有我利用它来编写代码实际需要它。

尽量避免花费时间微优化代码,直到你知道你有一个瓶颈。那时候,通常先测量,稍后再测量的提示仍然有效。