【问题标题】:Object equality behaves different in .NET对象相等在 .NET 中的行为不同
【发布时间】:2012-04-27 22:29:27
【问题描述】:

我有这些陈述,他们的结果就在附近。

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) 为假

【问题讨论】:

标签: c# .net


【解决方案1】:

== 运算符比较引用(内存地址),而 .Equals 比较实际对象值。在字符串的情况下,你很幸运,两个相同的字符串可以经常引用同一个地址。托管语言的乐趣之一。

【讨论】:

    【解决方案2】:

    在 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()

    【讨论】:

      【解决方案3】:

      当您说string1 == string2 时,比较使用string 类型的重载== 运算符,它比较字符串的值。

      当您说object1 == object2 时,即使在这种情况下它们是字符串,它们也不能作为字符串来查找运算符。所以比较使用默认的== 操作符,它比较references 是否相等。这意味着,如果两个对象不是完全相同的对象,它将返回 false。

      【讨论】:

        【解决方案4】:

        String 重载了相等运算符,以便您可以使用== 进行值比较。因此

        a == b //true.

        当您将它们向下转换为对象时,您只是在比较引用。 如果另一个字符串实例已经可用,String 会查看内部string-pool,否则将创建一个新实例并将其添加到池中。所以实际上abxy 甚至是同一个引用,这就是为什么

        x == y //true.

        使用Stringconstructor,即使存在另一个具有相同值(长度和字符序列)的字符串,您也会强制.NET 创建一个新实例。这就是为什么

        k == m //false

        http://en.csharp-online.net/CSharp_String_Theory%E2%80%94String_intern_pool

        【讨论】:

          【解决方案5】:

          正如C# FAQ on MSDN 所见 - 编译器无法使用重载方法并回退到比较引用。

          更大的问题是为什么它在第一个对象比较中成功。我最好的猜测是成功,因为 a 和 b 都给出了相同的参考。对于 c 和 d,您强制使用不同的引用。

          【讨论】:

            【解决方案6】:

            在字符串的情况下,== 运算符被重载以测试值是否相等,当使用 object 引用相等时。

            由于cd都是字符串,所以当你在km中使用Equals时,使用的是重载方法。

            由于上述原因,c == dtrue - 由于运算符重载,值相等用于 string 类型。

            【讨论】:

            • @Nick - 正如您发布的那样,字符串实习负责:)
            • 其实问题是如果 (k == m) 为假,为什么 (x == y) 为真?
            • @Ryu - 如果这就是你想问的,你为什么不问?
            • 如果我没有提到,我还没有提到“Console.Writeline(x == y); // true”部分。
            • @Oded,字符串实习适用于 string 文字。字符串 cd 不使用内部字符串,因为它们不是从文字创建的。
            【解决方案7】:
               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     <PrivateImplementationDetails>{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     <PrivateImplementationDetails>{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 指令将比较推入堆栈的值相等。压入堆栈的值是两个装箱字符串的引用,它们相等。

            【讨论】:

              【解决方案8】:
              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 
              

              这里我们检查两个引用是否相同......它们不是

              关键在于判断相等性何时比较引用或值。

              对象,除非另有重载,否则比较引用。

              结构,除非另有重载,否则比较值。

              【讨论】:

                【解决方案9】:

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

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

                c==d 为真,因为正在使用字符串相等运算符。

                【讨论】:

                • 根据你的回答,解释为什么c == d?
                猜你喜欢
                • 2011-01-07
                • 1970-01-01
                • 1970-01-01
                • 2020-06-22
                • 1970-01-01
                • 2020-08-03
                • 1970-01-01
                • 1970-01-01
                • 2015-11-16
                相关资源
                最近更新 更多