8

在下面的示例中,第三个评估返回 false,一切都很好,但第四个示例返回 true。
但是,我不太明白这是如何工作的,默认情况下Object.Equals比较对象相等的两个引用,并且看到 asab都指向一个唯一的一个字符串的实例,这应该返回 false,它在第三个示例中执行此操作,但在第四个示例中没有。
现在我明白了为什么它在第二个示例中返回 true,因为该.Equals()方法在字符串类中被覆盖,但在第四个示例中,我们将此字符串转换为对象。那么在这种情况下
它不会调用吗?Object.Equals

static void Main()
{
    // Create two equal but distinct strings
    string a = new string(new char[] {'h', 'e', 'l', 'l', 'o'});
    string b = new string(new char[] {'h', 'e', 'l', 'l', 'o'});

    Console.WriteLine (a == b); // Returns true
    Console.WriteLine (a.Equals(b)); // Returns true

    // Now let's see what happens with the same tests but
    // with variables of type object
    object c = a;
    object d = b;

    Console.WriteLine (c == d); // Returns false
    Console.WriteLine (c.Equals(d)); // Returns true
}
4

3 回答 3

10

线索是“默认情况下”这两个词。用自定义实现string覆盖。object.Equals由于object.Equals是多态方法 ( virtual// overrideetc),因此即使变量 (/expression) 类型为object.

==但是,不是多态的;使用的实现完全取决于变量(/表达式)类型。在这种情况下,由于已知类型是object,唯一可用的比较是引用相等。

也许更简洁:

class Foo {
    public override string ToString() { return "hi"; }
}
//...
object obj = new Foo();
string s = obj.ToString(); // this is "hi"

这是相同的原则:使用最派生的虚方法重载,而不管编译器知道的类型(object在这种情况下)。

于 2014-04-23T07:00:39.877 回答
1

Equals方法是虚拟的,这意味着即使在具有该object类型的引用上调用它,它最终也会调用实例的具体类型的实现。

从生成的 IL:a.Equals(b)变为

IL_003C:  ldloc.0     // a
IL_003D:  ldloc.1     // b
IL_003E:  callvirt    System.String.Equals

c.Equals(d)成为

IL_0057:  ldloc.2     // c
IL_0058:  ldloc.3     // d
IL_0059:  callvirt    System.Object.Equals

因此,两者都是分别对类型字符串和对象的引用的虚拟调用,并且都调用Equals方法,而不是在引用上,而是在实例本身上。

另一方面,a==b成为静态方法的调用System.String.op_Equality

IL_002F:  ldloc.0     // a
IL_0030:  ldloc.1     // b
IL_0031:  call        System.String.op_Equality

c==d变得公正

IL_004D:  ldloc.2     // c
IL_004E:  ldloc.3     // d
IL_004F:  ceq      

,对 IL 指令的调用,check-equal它只进行简单的引用检查。


您还可以验证是否使用以下代码调用了覆盖的实现:

class MyClass
{
  public override bool Equals(object obj)
  {
      Console.WriteLine("My Class Equals is called");
      return true;
  }
}

void Main()
{
   object a = new MyClass();
   object b = new MyClass();
   Console.WriteLine (a.Equals(b));
}

这将输出

My Class Equals is called
True
于 2014-04-23T07:00:29.847 回答
0

由于 Equals 是一个虚方法,任何实现了 equals 的类无论如何都会自动覆盖原来的 equals。如果你想使用 object.Equals,你必须使用object.ReferenceEquals(a,b).

有关更多信息,请查看虚拟方法的工作原理,如果您愿意的话,vtables 是如何实际实现的(一旦您掌握了它,这实际上很容易)

于 2014-04-23T06:59:49.760 回答