当我们在C#中初始化字符串时会发生什么?

时间:2015-12-07 08:54:44

标签: c# string

请查看以下程序:

static void Main()
{
    string s1 = "Hello";
    string s2 = "Hello";
    Console.WriteLine ( ( object ) s1 == ( object ) s2 );
    Console.ReadLine();
}

此代码段的输出为“TRUE”。现在我的问题是:

  1. string s1 = "HELLO" ;创建一个新的字符串对象吗?如果是,如何在不调用构造函数且不使用new运算符的情况下创建新对象?

  2. 如果string s1 = "HELLO"string s2 = "HELLO"创建了两个对象,那么答案为何如此?

2 个答案:

答案 0 :(得分:4)

如果你打算比较对象引用,那就更清楚了:

Console.WriteLine ( object.ReferenceEquals(s1, s2 ));

而不是像这样:

Console.WriteLine ( ( object ) s1 == ( object ) s3 ); // false

那就是说,让我们稍微改写你的代码:

using System;

public class Program
{
    public static void Main()
    {
        string s1 = "Hello";
        string s2 = string2();
        Console.WriteLine ( object.ReferenceEquals(s1, s2 )); // true

        string s3 = "Hel";
        s3 = s3 + "lo";

        Console.WriteLine ( object.ReferenceEquals(s1, s3 )); // false

        // This is the equivalent of the line above:
        Console.WriteLine ( ( object ) s1 == ( object ) s3 ); // also false

        Console.WriteLine (s1 == s3); // true (comparing string contents)

        s3 = string.Intern(s3);
        Console.WriteLine ( object.ReferenceEquals(s1, s3 )); // now true

        Console.ReadLine();
    }

    private static string string2()
    {
        return "Hello";
    }
}

好的,问题是,“为什么前两个字符串具有相同的引用”?

这个问题的答案是因为编译器保存了一个表到目前为止存储的所有字符串,如果遇到的新字符串已经存在于该表中,则它不会存储新的字符串。相反,它使新字符串引用已在其表中的相应字符串。这称为string interning

接下来要注意的是,如果通过在运行时连接两个字符串来创建新字符串,那么该新字符串与现有字符串的引用不同。创建一个全新的字符串。

但是,如果您使用==将该字符串与另一个具有不同引用但内容相同的字符串进行比较,则会返回true。那是因为字符串==比较字符串的内容

上面代码中的以下行说明了这一点:

Console.WriteLine (s1, s3); // true

最后,请注意运行时可以“实习”字符串,即使用对现有字符串的引用而不是新字符串。但是,它不会自动执行此操作。

您可以调用string.Intern()显式实习字符串,如上面的代码所示。

答案 1 :(得分:2)

  

字符串s1 =“HELLO”;创建一个新的字符串对象?如果是的话,如何   它是否在不调用构造函数的情况下创建了一个新对象   不使用new运算符??

是的,它不仅创建了一个新字符串,而且还将其加入“用户字符串”部分下的库元数据中(这又称为"string interning"),因此它可以在运行时直接将其从那里拉出来-time并保存分配时间。您可以使用ILDASM查看它:

User Strings
-------------------------------------------------------
70000001 : ( 5) L"Hello"

还可以看到编译器在解析语法树时将其识别为StringLiteralToken

LINQPad Roslyn Visualizer

编译器知道为字符串提供的特殊语法,并允许您使用特殊的语法糖。

  

如果字符串s1 =“HELLO”,而字符串s2 =“HELLO”则创建两个对象,   那怎么回答是真的?

正如我之前在第一部分中所说的,字符串文字实际上只在运行时加载。这意味着字符串将被加载一次,缓存并与自身进行比较,从而导致此引用相等性检查产生true。

您可以在发出的IL(在发布模式下编译)中看到这一点:

IL_0000:  ldstr       "Hello"
IL_0005:  ldstr       "Hello"
IL_000A:  stloc.0     // s2
IL_000B:  ldloc.0     // s2
IL_000C:  ceq