我读了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
我正在使用fw 4.5和vs2010。
幸运的是我也安装了vs2005,结果相同:
答案 0 :(得分:43)
以下是Eric Lippert撰写的一篇博文,回答了您的问题:String interning and 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
的实例,即使它被实习。 默认情况下只编译时间文字。这意味着以下代码打印为true:
var k1 = "k";
object k2 = "k";
Console.WriteLine(k1 == k2);
但是,如果您尝试在运行时以编程方式创建带有"k"
内容的字符串,例如使用string(char[])
构造函数,在对象上调用ToString()
,使用StringBuilder
等,默认情况下不会获得实习字符串。这个打印错误;
var k1 = "k";
object k2 = new string("k".ToCharArray());
Console.WriteLine(k1 == k2);
为什么呢?因为运行时的实习字符串很昂贵。
没有免费午餐这样的事情。
(...)
简而言之,一般情况下不值得实习所有字符串。
关于空字符串的不同行为:
某些版本的.NET运行时会在运行时自动生成空字符串,有些则不会!
答案 1 :(得分:11)
请注意,在的第二个代码块中实习 new 字符串会使它们相等。
var k="k";
object x = string.Intern(new string(k.ToArray()));
object y = string.Intern(new string(k.ToArray()));
Console.WriteLine(x == y); //true
似乎它是自动实现空字符串,但非空字符串不会被实现,除非它们是明确地完成的(或者它们是始终被实现的文字字符串)。
我猜是的,空字符串被视为一种特殊情况并被自动实施,可能是因为检查是如此微不足道,以至于它不会增加任何真正的性能损失(我们可以安全地说任何字符串length 0是空字符串,与任何其他空字符串相同 - 所有其他字符串都要求我们查看字符而不仅仅是长度。
答案 2 :(得分:6)
第一种情况比较了对同一对象(String.Empty
)的2个引用。为2 operator==
个变量调用object
会导致他们通过引用进行比较并给出true
。
第二种情况产生2个不同的字符串类实例。他们的参考比较给出false
如果您在第二种情况下将string
类型提供给x
和y
,则会调用string.operator==
覆盖,并且比较结果为true
请注意,在这两种情况下我们都不直接处理字符串实习。我们比较的字符串对象是使用string(char[])
构造函数创建的。显然,构造函数被设计为在使用空数组作为参数调用时返回string.Empty
字段的值。
MarcinJuraszek发布的答案引用了讨论字符串实习的Lippert's blog。该博客文章讨论了字符串类使用的其他角落案例。考虑前面提到的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?!
我们在这里看到的是,空字符串文字(""
)的赋值不能保证产生对静态只读System.String.Empty
字段的引用。
让我们看一下object x = new string("".ToArray());
表达式的IL:
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
类作为引用类型并且同时不可变的副作用之一。还有其他不可变框架类型具有类似语义的预定义值(如DateTime.MinValue
)。但据我所知,这些框架类型被定义为struct
,而不是作为引用类型的string
。值类型是完全不同的故事......从可变类构造函数返回一些固定的预定义类型实例是没有意义的(调用代码将能够更改该实例并导致该类型的不可预测的行为)。因此,如果这些类型是不可变的,那么构造函数并不总是返回新实例的引用类型可能存在。我不知道框架中的其他类型,除了string
。
答案 3 :(得分:4)
我的假设是为什么第一个产生真,而第二个产生错误:
第一个结果是我的优化,请使用以下代码
Enumerable.Empty<char>() == Enumerable.Empty<char>() // true
因此,假设ToArray
方法在字符串为空时返回Enumerable.Empty<char>()
,这可以解释为什么第一个结果产生true而第二个结果没有,因为它正在进行参考检查。
答案 4 :(得分:2)
根据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
这意味着,即使从空数组构造,空字符串也会默认实现,因此是相等的。
此外:
The .NET Framework version 2.0 introduces the CompilationRelaxations.NoStringInterning enumeration member
这很可能为您提供了一种创建一致的比较方式的方法,尽管如@BenM建议的那样,您宁愿明确使用Intern函数。
鉴于发生拳击,您还可以使用string.Equals
代替==
答案 5 :(得分:2)
我认为这可能是我引用 Jon Skeet 答案的原因 关于字符串比较
Are string.Equals() and == operator really same?
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
答案 6 :(得分:2)
有一种特殊情况,空字符串总是返回相同的对象,这就是为什么当你比较对象是否相同时,在这种情况下它是真的。
[编辑]:之前的代码使用字符串比较器而不是对象
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