使用加号时会创建多少个String对象?

The*_*ght 115 c# string clr

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

String result = "1" + "2" + "3" + "4";
Run Code Online (Sandbox Code Playgroud)

如果它如下,我会说三个String对象:"1","2","12".

String result = "1" + "2";
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*ain 161

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

如果您在方法中执行此操作:

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

然后编译器似乎使用String.Concat@Joachim回答(+1给他btw)发出代码.

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

const String one = "1";
const String two = "2";
const String result = one + two + "34";
Run Code Online (Sandbox Code Playgroud)

或作为文字,如原始问题:

String result = "1" + "2" + "3" + "4";
Run Code Online (Sandbox Code Playgroud)

然后编译器将优化那些+迹象.它相当于:

const String result = "1234";
Run Code Online (Sandbox Code Playgroud)

此外,编译器将删除无关的常量表达式,并且只有在使用或公开它们时才会发出它们.例如,这个程序:

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

public static void main(string[] args) {
    Console.Out.WriteLine(result);
}
Run Code Online (Sandbox Code Playgroud)

只生成一个字符串 - 常量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();
    }
}
Run Code Online (Sandbox Code Playgroud)

在字符串在循环中连接(或以其他方式动态)的情况下,每个连接最终会有一个额外的字符串.例如,以下创建了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();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是(也令人惊讶地),编译器将多个连续的连接组合成单个多字符串连接.例如,该程序也只生成12个字符串实例!这是因为" 即使你在一个语句中使用了几个+运算符,字符串内容也只复制一次. "

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


Eri*_*ert 85

Chris Shain的回答非常好.作为编写字符串连接优化器的人,我只想添加两个额外的有趣点.

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

string s = M() + "A" + "B";
Run Code Online (Sandbox Code Playgroud)

然后编译器认为加法运算符是左关联的,因此它与:

string s = ((M() + "A") + "B");
Run Code Online (Sandbox Code Playgroud)

但是这个:

string s = "C" + "D" + M();
Run Code Online (Sandbox Code Playgroud)

是相同的

string s = (("C" + "D") + M());
Run Code Online (Sandbox Code Playgroud)

所以这是的串联字符串常量 "CD"M().

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

你甚至可以这样做:

string s = (M() + "E") + ("F" + M()));
Run Code Online (Sandbox Code Playgroud)

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

第二个有趣的观点是null和空字符串被优化掉了.所以,如果你这样做:

string s = (M() + "") + (null + M());
Run Code Online (Sandbox Code Playgroud)

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

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

string s = M() + null;
Run Code Online (Sandbox Code Playgroud)

我们无法优化它

string s = M();
Run Code Online (Sandbox Code Playgroud)

因为M()可能返回null,但String.Concat(M(), null)如果M()返回null 则返回空字符串.所以我们所做的就是减少

string s = M() + null;
Run Code Online (Sandbox Code Playgroud)

string s = M() ?? "";
Run Code Online (Sandbox Code Playgroud)

从而证明字符串连接根本不需要实际调用String.Concat.

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

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

  • 起始段落+1.:)像这样的答案总是让我惊讶Stack Overflow. (21认同)

Dav*_*vid 23

我在MSDN上找到了答案.一.

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

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


Jar*_*Par 22

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

String result = "1234";
Run Code Online (Sandbox Code Playgroud)


Joa*_*son 13

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

如果它们是动态的,那么它们已被优化为对String.Concat的单个调用(字符串,字符串,字符串,字符串).


Mis*_*ble 13

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

  • 至少对于VS 2008和2010的Microsoft C#编译器来说,这是记录在案的行为(参见@ David-Stratton的回答).那就是说,你是对的 - 据我所知,通过快速阅读,C#规范没有指明这一点,它可能应该被视为一个实现细节. (3认同)