.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引用相等时, ==运算符被重载以测试值相等性。

由于cd 字符串,因此在km使用Equals时,将使用重载方法。

并且c == d对于上面的状态是true的 – 当运算符重载时,对string类型使用值相等。

因为它们是两个不同的对象引用。 对此的内置比较是比较它们是否指向相同的实际对象。

由于String Interningab都是对同一字符串对象的引用。

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查找内部字符串池 ,否则将创建一个新实例并将其添加到池中。 所以实际上abxy甚至是相同的参考,这就是原因

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比较实际的对象值。 在字符串的情况下,你很幸运,两个相同的字符串可以经常引用相同的地址。 托管语言的乐趣之一。