在下面的代码中使用加号时会创建多少个String对象?
String result = "1" + "2" + "3" + "4";
如果如下所示,我会说三个String对象:“1”,“2”,“12”。
String result = "1" + "2";
我也知道String对象被缓存在String Intern Pool / Table中以提高性能,但这不是问题。
答案 0 :(得分:161)
令人惊讶的是,这取决于。
如果您在方法中执行此操作:
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";
此外,编译器将删除无关的常量表达式,并且只有在使用或公开它们时才会发出它们。例如,这个程序:
const String one = "1";
const String two = "1";
const String result = one + two + "34";
public static void main(string[] args) {
Console.Out.WriteLine(result);
}
仅生成一个字符串 - 常量result
(等于“1234”)。 one
和two
未显示在生成的IL中。
请记住,运行时可能会进一步优化。我只是想了解IL的产生。
最后,关于实习,常量和文字是实习的,但是实习的值是IL中的结果常量值,而不是文字。这意味着您可能会获得比预期更少的字符串对象,因为多个相同定义的常量或文字实际上将是同一个对象!这由以下说明:
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();
}
}
如果字符串在循环中连接(或以其他方式动态连接),则每次连接最终会有一个额外的字符串。例如,以下创建了12个字符串实例: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个字符串实例!这是因为“Even if you use several + operators in one statement, the string content is copied only once.”
public class Program
{
static void Main(string[] args)
{
string result = "";
for (int i = 0; i < 10; i++)
result += "a" + result;
Console.ReadKey();
}
}
答案 1 :(得分:86)
Chris Shain的回答非常好。作为编写字符串连接优化器的人,我只想添加两个有趣的点。
首先,串联优化器在它可以安全地执行此操作时基本上忽略括号和左关联性。假设您有一个返回字符串的方法M()。如果你说:
string s = M() + "A" + "B";
然后编译器认为加法运算符是左关联的,因此它与:
相同string s = ((M() + "A") + "B");
但是这个:
string s = "C" + "D" + M();
与
相同string s = (("C" + "D") + M());
这是常量字符串 "CD"
与M()
的串联。
实际上,连接优化器意识到字符串连接是关联,并为第一个例子生成String.Concat(M(), "AB")
,即使这违反了左关联性。
你甚至可以这样做:
string s = (M() + "E") + ("F" + M()));
我们仍会生成String.Concat(M(), "EF", M())
。
第二个有趣的观点是null和空字符串被优化掉了。所以,如果你这样做:
string s = (M() + "") + (null + M());
你会得到String.Concat(M(), M())
然后提出了一个有趣的问题:这是怎么回事?
string s = M() + null;
我们无法将其优化为
string s = M();
因为M()
可能返回null,但如果String.Concat(M(), null)
返回null,则M()
将返回空字符串。所以我们所做的就是减少
string s = M() + null;
到
string s = M() ?? "";
从而证明字符串连接实际上根本不需要调用String.Concat
。
有关此主题的进一步阅读,请参阅
答案 2 :(得分:23)
我在MSDN上找到了答案。之一。
How to: Concatenate Multiple Strings (C# Programming Guide)
连接是将一个字符串附加到末尾的过程 另一个字串。连接字符串文字或字符串时 常量使用+运算符,编译器创建一个 串。没有运行时连接发生。但是,字符串变量 只能在运行时连接。在这种情况下,你应该 了解各种方法的性能影响。
答案 3 :(得分:22)
只有一个。 C#编译器将折叠字符串常量,因此它基本上编译为
String result = "1234";
答案 4 :(得分:13)
一,由于它们是静态的,编译器将能够在编译时将其优化为单个字符串。
如果它们是动态的,那么它们已针对String.Concat(string, string, string, string)的一次调用进行了优化。
答案 5 :(得分:13)
我怀疑这是任何标准或规范的强制要求。一个版本可能会做一些不同的事情。