How many String objects will be created when using a plus sign?

在下面的代码中使用加号将创建多少个 String 对象?

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

如果如下所示,我会说三个 String 对象: “1”,“2”,“12”。

String result = "1" + "2";

我还知道 String 对象缓存在 String Intern Pool/Table 中以提高性能,但这不是问题所在。

5665 次浏览

第一,由于它们是静态的,编译器将能够在编译时将其优化为单个字符串。

如果它们是动态的,它们将被优化为对 Concat (String,String,String,String)的单个调用。

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

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

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

然后编译器似乎使用 String.Concat发出代码,就像@Joachim 回答的那样(顺便说一句,+ 1)。

如果你把它们定义为 constants,例如:

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

或作为 字面意思,如在最初的问题:

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

然后编译器就会优化掉这些 +符号,这相当于:

const String result = "1234";

Furthermore, the compiler will remove extraneous constant expressions, and only emit them if they are used or exposed. For instance, this program:

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”)。onetwo在产生的 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();
}
}

But (also surprisingly), multiple consecutive concatenations are combined by the compiler into a single multi-string concatenation. For example, this program also only produces 12 string instances! This is because "即使在一个语句中使用多个 + 运算符,字符串内容也只复制一次。"

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

我在 MSDN 上找到了答案,一。

如何: 连接多个字符串(C # 编程指南)

连接是将一个字符串追加到 当串联字符串文字或字符串时 constants by using the + operator, the compiler creates a single 没有运行时串联发生。但是,字符串变量 只能在运行时连接。在这种情况下,您应该 理解各种方法的性能影响。

只有一个。 C # 编译器将折叠字符串常量,因此它基本上编译到

String result = "1234";

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

Chris Shain 的回答非常好。作为编写字符串连接优化器的人,我只想再加上两点有趣的地方。

第一个问题是,连接优化器在可以安全地做到这一点时,基本上忽略了括号和左关联性。假设您有一个返回字符串的方法 M ()。如果你说:

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

然后编译器推断加法运算符是左结合的,因此这与:

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

But this:

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

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

这就是 常数字符串 "CD"M()的串联。

实际上,连接优化器认识到字符串连接是 联想,并为第一个示例生成 String.Concat(M(), "AB"),即使这违反了左关联性。

你甚至可以这样做:

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

and we'll still generate String.Concat(M(), "EF", M()).

第二个有趣的地方是,空字符串和空字符串被优化掉了:

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 s = M() + null;

to

string s = M() ?? "";

从而说明字符串串联实际上根本不需要调用 String.Concat

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

为什么 String.Concat 没有优化到 StringBuilder?