繁体   English   中英

在c#中添加字符串,编译器如何做到这一点?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM