使用加号时将创建多少个 String 对象
本文关键字:多少 String 对象 创建 | 更新日期: 2023-09-27 18:22:19
在下面的代码中使用加号时将创建多少个 String 对象?
String result = "1" + "2" + "3" + "4";
如果如下所示,我会说三个字符串对象:"1"、"2"、"12"。
String result = "1" + "2";
我也知道字符串对象缓存在字符串实习生池/表中以提高性能,但这不是问题。
令人惊讶的是,这取决于。
如果在某个方法中执行此操作:
void Foo() {
String one = "1";
String two = "2";
String result = one + two + "34";
Console.Out.WriteLine(result);
}
然后编译器似乎使用String.Concat
发出代码,因为@Joachim回答(+1 给他顺便说一句(。
如果将它们定义为常量,例如:
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 个字符串实例! 这是因为"即使在一个语句中使用多个 + 运算符,字符串内容也只会复制一次。
public class Program
{
static void Main(string[] args)
{
string result = "";
for (int i = 0; i < 10; i++)
result += "a" + result;
Console.ReadKey();
}
}
Chris Shain的回答非常好。作为编写字符串连接优化器的人,我只想添加两个有趣的点。
首先,串联优化器在可以安全地这样做时基本上忽略了括号和左关联性。假设您有一个返回字符串的方法 M((。如果你说:
string s = M() + "A" + "B";
然后编译器认为加法运算符是左关联的,因此这与
:string s = ((M() + "A") + "B");
但是这个:
string s = "C" + "D" + M();
与
string s = (("C" + "D") + M());
所以这就是常量字符串与M()
的串联"CD"
。
事实上,串联优化器意识到字符串连接是关联的,并为第一个示例生成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,但如果返回 null,String.Concat(M(), null)
会返回空字符串M()
。 所以我们所做的是减少
string s = M() + null;
自
string s = M() ?? "";
从而证明字符串串联实际上根本不需要调用String.Concat
。
有关此主题的进一步阅读,请参阅
为什么 String.Concat 没有针对 StringBuilder.Append 进行优化?
我在MSDN找到了答案。 一。
如何:连接多个字符串(C# 编程指南(
串联是将一个字符串附加到末尾的过程另一个字符串。连接字符串文本或字符串时常量 通过使用 + 运算符,编译器将创建一个字符串。不发生运行时串联。但是,字符串变量只能在运行时连接。在这种情况下,您应该了解各种方法的性能影响。
只有一个。 C# 编译器将折叠字符串常量,因此它基本上编译为
String result = "1234";
我怀疑这是任何标准或规范规定的。一个版本可能会做一些与另一个版本不同的事情。
首先,由于它们是静态的,编译器将能够在编译时将其优化为单个字符串。
如果它们是动态的,它们将被优化为对 String.Concat(string, string, string, string( 的单个调用。