.NET中的对象相等性行为不同
我有这些陈述,他们的结果就在他们附近。
string a = "abc"; string b = "abc"; Console.Writeline(a == b); //true object x = a; object y = b; Console.Writeline(x == y); // true string c = new string(new char[] {'a','b','c'}); string d = new string(new char[] {'a','b','c'}); Console.Writeline(c == d); // true object k = c; object m = d; Console.Writeline(k.Equals(m)) //true Console.Writeline(k == m); // false
为什么最后的平等给我假?
问题是为什么(x == y)为真(k == m)为假
string a = "abc"; string b = "abc"; Console.Writeline(a == b); //true
由于String Interning,字符串引用对于相同的字符串是相同的
object x = a; object y = b; Console.Writeline(x == y); // true
因为引用是相同的,所以从同一引用创建的两个对象也是相同的。
string c = new string(new char[] {'a','b','c'}); string d = new string(new char[] {'a','b','c'});
在这里创建两个新的字符数组,这些引用是不同的。
Console.Writeline(c == d); // true
字符串重载==按值进行比较。
object k = c; object m = d;
由于先前的引用不同,因此这些对象是不同的。
Console.Writeline(k.Equals(m)) //true
.Equals
使用重载的String equals
方法,该方法再次按值进行比较
Console.Writeline(k == m); // false
在这里,我们检查两个引用是否相同……它们不是
关键是确定何时相等性比较参考或值。
除非另有重载,否则对象将比较引用。
除非重载,否则结构将比较值。
对于字符串,当使用object
引用相等时, ==
运算符被重载以测试值相等性。
由于c
和d
是字符串,因此在k
和m
使用Equals
时,将使用重载方法。
并且c == d
对于上面的状态是true
的 – 当运算符重载时,对string
类型使用值相等。
因为它们是两个不同的对象引用。 对此的内置比较是比较它们是否指向相同的实际对象。
由于String Interning , a
和b
都是对同一字符串对象的引用。
c==d
为true,因为正在使用字符串相等运算符。
string c = new string(new char[] {'a','b','c'}); string d = new string(new char[] {'a','b','c'}); Console.WriteLine(c == d); // true object k = c; object m = d; Console.WriteLine(k.Equals(m)); //true Console.WriteLine(k == m); // false
生成IL代码,如下所示:
IL_0001: ldc.i4.3 IL_0002: newarr System.Char IL_0007: dup IL_0008: ldtoken {61BB33F4-0CA5-4733-B259-764124AD1A79}.$$method0x6000002-1 IL_000D: call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray IL_0012: newobj System.String..ctor IL_0017: stloc.0 IL_0018: ldc.i4.3 IL_0019: newarr System.Char IL_001E: dup IL_001F: ldtoken {61BB33F4-0CA5-4733-B259-764124AD1A79}.$$method0x6000002-2 IL_0024: call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray IL_0029: newobj System.String..ctor IL_002E: stloc.1 IL_002F: ldloc.0 IL_0030: ldloc.1 IL_0031: call System.String.op_Equality //STRING EQUALITY IL_0036: call System.Console.WriteLine IL_003B: nop IL_003C: ldloc.0 IL_003D: stloc.2 IL_003E: ldloc.1 IL_003F: stloc.3 IL_0040: ldloc.2 IL_0041: ldloc.3 IL_0042: callvirt System.Object.Equals IL_0047: call System.Console.WriteLine IL_004C: nop IL_004D: ldloc.2 IL_004E: ldloc.3 IL_004F: ceq //CEQ INSTRUCTION: **VALUES** EQUALITY ! IL_0051: call System.Console.WriteLine
正如您所看到的那样,最后一条指令调用CEQ指令,使得在堆栈上推送的值相等。 在堆栈上推送的值是两个盒装字符串的引用 ,它们不相等。
正如MSDN上的C#FAQ所见 – 编译器无法使用重载方法,而是回退到比较引用。
更大的问题是为什么它在第一次对象比较中取得成功。 我最好的猜测是成功,因为a和b都给出了相同的参考。 对于c和d,您强制使用不同的引用。
String
重载了相等运算符,以便您可以使用==
进行值比较。 于是
a == b //true
。
当你将它们向下转换为对象时,你只是在比较引用。 如果另一个字符串实例已经可用, String
查找内部字符串池 ,否则将创建一个新实例并将其添加到池中。 所以实际上a
, b
, x
和y
甚至是相同的参考,这就是原因
x == y //true
。
使用String
的构造函数 ,即使存在具有相同值(长度和字符序列)的另一个字符串,也会强制.NET创建新实例。 这就是为什么
k == m //false
http://en.csharp-online.net/CSharp_String_Theory%E2%80%94String_intern_pool
当你说string1 == string2
,比较使用string
类型的重载==
运算符,它比较字符串的值。
当你说object1 == object2
,即使它们在这种情况下是字符串,它们也不能作为字符串来查找运算符。 因此,比较使用默认的==
运算符,该运算符比较相等的引用 。 这意味着,如果两个对象不是完全相同的对象,则返回false。
在Bob2Chiv的基础上,我尝试了VB中的等价物(VS2010):
Dim a As String = "abc" Dim b As String = "abc" Console.WriteLine(a = b) ' True Dim x As Object = a Dim y As Object = b Console.WriteLine(x = y) ' True Dim c As String = New String(New Char() {"a"c, "b"c, "c"c}) Dim d As String = New String(New Char() {"a"c, "b"c, "c"c}) Console.WriteLine(c = d) ' True Dim k As Object = c Dim m As Object = d Console.WriteLine(k.Equals(m)) ' True Console.WriteLine(k = m) ' True (Yes, True!!) Console.WriteLine(k Is m) ' False (Like in C#) Console.WriteLine(a Is b) ' True (Like in C#)
(至少我认为这是相同的 – 我欢迎对此进行纠正。)
道德?:在C#中注意==
– 当值的比较符合要求时,更喜欢.Equals()
?
==
运算符比较引用(内存地址),而.Equals比较实际的对象值。 在字符串的情况下,你很幸运,两个相同的字符串可以经常引用相同的地址。 托管语言的乐趣之一。