[英]addition of strings in c#, how the compiler does it?
A = string.Concat("abc","def")
B = "abc" + "def"
A与B.
最近我很困惑为什么很多人会说,与B相比,A的处理速度要快得多。但是,他们只会说,因为有人这么说或者因为它就是这样。 我想我可以从这里听到更好的解释。
编译器如何处理这些字符串?
谢谢!
当我加入C#编译器团队时,我做的第一件事就是重写了字符串连接的优化器。 美好的时光。
如前所述,常量字符串的字符串连接在编译时完成。 非常量字符串做一些奇特的东西:
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() ?? ""
干净,嗯?
在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")
实际上,B在编译期间被解析。 最终将得到B = "abcdef"
而对于A,连接将推迟到执行时间。
如果字符串是文字,就像你的问题一样,那么分配给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);
在这种特殊情况下,两者实际上是相同的。 编译器将第二个变量(使用+
运算符)转换为对第一个变体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只是因为我知道这种情况,但这并不是说我编写的所有使用它的代码实际上都需要它。
尽量避免花时间微优化您的代码,直到您知道自己有瓶颈为止。 那个时候,关于措施的通常提示,后来削减,仍然有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.