使用加号时会创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”)。 one
和two
不会出现在最终的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) 。