空string作为特例?

我读了Jon Skeet的测验,我想知道为什么我的第二个样本不会在第一个样本中工作。

为什么这是true

 object x = new string("".ToArray()); object y = new string("".ToArray()); Console.WriteLine(x == y); //true 

但是这个不是:

 var k="k"; //string.intern(k); // doesn't help object x = new string(k.ToArray()); object y = new string(k.ToArray()); Console.WriteLine(x == y); //false 

我在vs2010中使用了fw 4.5。

幸运的是我也安装了vs2005,结果相同:

在这里输入图像说明

这里是Eric Lippert的博客文章,它回答你的问题: String interning和String.Empty 。

他正在描述类似的情况:

 object obj = "Int32"; string str1 = "Int32"; string str2 = typeof(int).Name; Console.WriteLine(obj == str1); // true Console.WriteLine(str1 == str2); // true Console.WriteLine(obj == str2); // false !? 

所以这个想法是,实习并不意味着即使实习期间你只会有一个特定string实例。 只有编译时间文字是默认实现的 。 这意味着下面的代码打印真实:

 var k1 = "k"; object k2 = "k"; Console.WriteLine(k1 == k2); 

但是,如果您尝试在运行时以编程方式创build带有"k"内容的string(char[]) ,例如使用string(char[])构造函数,使用StringBuilder等在对象上调用ToString() ,则默认情况下不会获取internedstring。 这个打印错误;

 var k1 = "k"; object k2 = new string("k".ToCharArray()); Console.WriteLine(k1 == k2); 

为什么? 因为在运行时实习string是昂贵的。

没有这样的免费午餐。

(……)

总之,一般情况下都不值得实习。

关于空string的不同行为:

.NET运行库的某些版本会在运行时自动实习空string,有些则不会!

请注意,在第二个代码块中实施新的string会使它们相等。

 var k="k"; object x = string.Intern(new string(k.ToArray())); object y = string.Intern(new string(k.ToArray())); Console.WriteLine(x == y); //true 

它似乎是自动实习空string,但非空string不会被实施,除非它们明确地完成(或者它们是始终被实施的string)。

我猜是的,空string被视为一个特殊的情况,并自动实现,可能是因为检查是如此微不足道的,它不会增加任何真正的性能损失(我们可以安全地说,任何长度为0的string是空string和任何其他空string相同 – 所有其他string都要求我们查看字符而不仅仅是长度)。

第一种情况比较2个引用到同一个对象( String.Empty )。 为2个objectvariables调用operator== ,通过引用引起它们的比较,并给出true

第二种情况产生2个不同的string类的实例。 他们的参考比较给出false

如果在第二种情况下将stringtypes赋予xy ,则将调用string.operator==覆盖,比较结果为true

请注意,我们不直接在两种情况下处理stringinterning。 我们比较的string对象是使用string(char[])构造函数创build的。 显然,构造函数被devise为当用空数组作为参数调用时返回string.Empty字段的值。

MarcinJuraszek发表的回答是关于Lippert的博客 ,该博客讨论了string实习。 那篇博文讨论了string类用法的其他一些情况。 从前面提到的Lippert的博客来看这个例子:

 object obj = ""; string str1 = ""; string str2 = String.Empty; Console.WriteLine(obj == str1); // true Console.WriteLine(str1 == str2); // true Console.WriteLine(obj == str2); // sometimes true, sometimes false?! 

我们在这里看到的是,来自空string文字( "" )的赋值不能保证产生对静态只读System.String.Empty字段的引用。

让我们来看一下IL object x = new string("".ToArray()); expression:

 IL_0001: ldstr "" IL_0006: call !!0[] [System.Core]System.Linq.Enumerable::ToArray<char>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) IL_000b: newobj instance void [mscorlib]System.String::.ctor(char[]) IL_0010: stloc.0 

实习可能(或可能不)发生在IL_0001线。 ToArray()方法产生一个新的空数组, String::.ctor(char[])向我们提供String.Empty

我们在这里看到的并不是string.Empty的特殊情况,而是string类作为引用types和不可变同时发生的副作用之一。 还有其他不可变的框架types,它们具有类似语义的预定义值(如DateTime.MinValue )。 但据我所知,这样的框架types被定义为struct不同于引用types的string 。 值types是完全不同的故事…从可变类构造函数返回一些固定的预定义types实例(调用代码将能够更改该实例并导致该types的不可预知的行为)是没有意义的。 因此,如果这些types是不可变的,那么其构造函数并不总是返回新实例的引用types可能存在。 我不知道框架中的其他types,除了string

我的假设是为什么第一个产生错误而第二个产生错误:

第一个结果我是一个优化,采取下面的代码

 Enumerable.Empty<char>() == Enumerable.Empty<char>() // true 

所以,假设ToArray方法在string为空时返回Enumerable.Empty<char>() ,这可以解释为什么第一个结果是真实的,第二个结果是否为真,因为它正在执行引用检查。

根据http://msdn.microsoft.com/en-us/library/system.string.intern(v=vs.110).aspx

In the .NET Framework 3.5 Service Pack 1, the Intern method reverts to its behavior in the .NET Framework 1.0 and 1.1 with regard to interning the empty string...

...In the .NET Framework 1.0, .NET Framework 1.1, and .NET Framework 3.5 SP1, ~empty strings~ are equal

这意味着,即使从空数组构造时,空string也是默认实现的,因此是相等的。

此外:

The .NET Framework version 2.0 introduces the CompilationRelaxations.NoStringInterning enumeration member

这很可能为您提供了一种创build一致的方式进行比较的方法,尽pipe按照@BenM的build议,您将更愿意使用Intern函数。

鉴于发生的拳击,你也可以使用string.Equals而不是==

我想这可以是我指的Jon Skeet答案关于string比较的原因

string.Equals()和==运算符是否真的一样?

  object x1 = new StringBuilder("").ToString().ToArray(); object y1 = new StringBuilder("").ToString().ToArray(); Console.WriteLine(x1 == y1); //true Console.WriteLine("Address x1:" + Get(x1)); Console.WriteLine("Address y1:" + Get(y1)); var k = "k"; //string.intern(k); // doesn't help object x = new string(k.ToArray()); object y = new string(k.ToArray()); Console.WriteLine(x == y); //false Console.WriteLine("Address x:" + Get(x)); Console.WriteLine("Address y:" + Get(y)); Console.Read(); 

产量

 False Address x1:0x2613E5 Address y1:0x2613E5 False Address x:0x2613E5 Address y:0x2613E5 

有一个特殊情况,空string总是返回相同的对象,这就是为什么当比较时,如果对象是相同的,在这种情况下,它是真实的。

[编辑]:以前的代码是使用string比较而不是对象

 object a = "s"; object b = "d"; a = ((string)a).Replace("s", ""); b = ((string)b).Replace("d", ""); Console.WriteLine(a == b); object c = "sa"; object d = "da"; c = ((string)c).Replace("s", ""); d = ((string)d).Replace("d", ""); Console.WriteLine(c == d); c = ((string)c).Replace("a", ""); d = ((string)d).Replace("a", ""); Console.WriteLine(c == d); 

结果

 True False True