使用加号时会创build多less个String对象?

在下面的代码中使用加号时会创build多less个String对象?

String result = "1" + "2" + "3" + "4"; 

如果是这样,我会说三个string对象:“1”,“2”,“12”。

 String result = "1" + "2"; 

我也知道String对象被caching在String Intern Pool / Table中以提高性能,但这不是问题。

令人惊讶的是,这取决于。

如果你在一个方法中做到这一点:

 void Foo() { String one = "1"; String two = "2"; String result = one + two + "34"; Console.Out.WriteLine(result); } 

那么编译器似乎发出了使用String.Concat作为@Joachim回答(+1给他btw)的代码。

如果你将它们定义为常量 ,例如:

 const String one = "1"; const String two = "2"; const String result = one + two + "34"; 

或作为文字 ,就像原来的问题一样:

 String result = "1" + "2" + "3" + "4"; 

那么编译器会优化掉那些+符号。 这相当于:

 const String result = "1234"; 

此外,编译器将删除多余的常量expression式,只有在使用或暴露的情况下才会发出。 比如这个程序:

 const String one = "1"; const String two = "1"; const String result = one + two + "34"; public static void main(string[] args) { Console.Out.WriteLine(result); } 

只生成一个string – 常量result (等于“1234”)。 onetwo不会出现在最终的IL中。

请记住,在运行时可能会进一步优化。 我只是按照什么产生IL。

最后,就实习而言,常数和文字是实习的,但被实习的价值是在实习生中产生的不变价值,而不是文字。 这意味着你可能会得到比你期望的更less的string对象,因为多个相同定义的常量或文字实际上是同一个对象! 如下所示:

 public class Program { private const String one = "1"; private const String two = "2"; private const String RESULT = one + two + "34"; static String MakeIt() { return "1" + "2" + "3" + "4"; } static void Main(string[] args) { string result = "1" + "2" + "34"; // Prints "True" Console.Out.WriteLine(Object.ReferenceEquals(result, MakeIt())); // Prints "True" also Console.Out.WriteLine(Object.ReferenceEquals(result, RESULT)); Console.ReadKey(); } } 

如果string在一个循环中(或者dynamic地)连接在一起,那么每个连接最后会有一个额外的string。 例如,下面创build12个string实例:2个常量+10个迭代,每个迭代产生一个新的String实例:

 public class Program { static void Main(string[] args) { string result = ""; for (int i = 0; i < 10; i++) result += "a"; Console.ReadKey(); } } 

但是(也令人惊讶地),编译器将多个连续的连接组合成单个多串连接。 例如,这个程序也只能产生12个string实例! 这是因为“ 即使在一个语句中使用多个+运算符,string内容也只能被复制一次”。

 public class Program { static void Main(string[] args) { string result = ""; for (int i = 0; i < 10; i++) result += "a" + result; Console.ReadKey(); } } 

克里斯·谢恩的回答非常好。 作为编写string连接优化器的人,我只需要添加两个有趣的点。

首先,串联优化器可以安全地忽略括号和左结合。 假设你有一个返回一个string的方法M()。 如果你说:

 string s = M() + "A" + "B"; 

那么编译器的原因是添加运算符是关联的,因此这与以下内容相同:

 string s = ((M() + "A") + "B"); 

但是这个:

 string s = "C" + "D" + M(); 

是相同的

 string s = (("C" + "D") + M()); 

所以这是常量string "CD"M()的串联。

实际上,连接优化器意识到string连接是关联的 ,并且为第一个示例生成String.Concat(M(), "AB") ,即使违反了左结合。

你甚至可以这样做:

 string s = (M() + "E") + ("F" + M())); 

我们仍然会生成String.Concat(M(), "EF", M())

第二个有趣的地方是空string被优化了。 所以,如果你这样做:

 string s = (M() + "") + (null + M()); 

你会得到String.Concat(M(), M())

然后提出一个有趣的问题:这个怎么样?

 string s = M() + null; 

我们无法优化

 string s = M(); 

因为M()可能返回null,但是如果M()返回null,则String.Concat(M(), null)将返回一个空string。 所以我们做的是减less

 string s = M() + null; 

 string s = M() ?? ""; 

从而certificatestring连接不需要实际调用String.Concat

有关此主题的进一步阅读,请参阅

为什么String.Concat未针对StringBuilder.Append进行优化?

我在MSDNfind了答案。 一。

如何连接多个string(C#编程指南)

连接是将一个string追加到另一个string的末尾的过程。 当通过使用+运算符连接string文字或string常量时,编译器会创build一个string。 没有运行时串联发生。 但是,stringvariables只能在运行时连接。 在这种情况下,您应该了解各种方法的性能影响。

只有一个。 C#编译器将折叠string常量,因此它本质上编译为

 String result = "1234"; 

我怀疑这是任何标准或规范的要求。 一个版本可能会做一些不同的事情。

其一,由于它们是静态的,因此编译器将能够在编译时将其优化为单个string。

如果他们是dynamic的,他们已经被优化到一个单一的调用String.Concat(string,string,string,string) 。