覆盖等于和类型转换

时间:2014-04-23 06:56:11

标签: c# polymorphism equality unary-operator

在下面的示例中,第三个评估返回false,一切都很好,但第四个示例返回true ..
我不太明白这是如何工作的,但默认情况下Object.Equals比较两个对象相等的引用,并看到ab都指向一个字符串的唯一实例,这应该返回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
}

3 个答案:

答案 0 :(得分:10)

线索是&#34;默认情况下&#34;。 string使用自定义实现覆盖object.Equals。由于object.Equals是一种多态方法(virtual / override / etc),即使变量(/ expression)类型为object,也会使用派生最多的实现。< / p> 然而,

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

也许更简洁:

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

这是相同的原则:使用虚拟方法的派生最多重载,无论编译器知道哪种类型(在这种情况下为object)。

答案 1 :(得分: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      

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


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

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

答案 2 :(得分:0)

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

有关虚拟方法如何工作的更多信息,以及如果您对此感到满意,那么实际上如何实现vtable(一旦您掌握了它,这实际上非常简单)