在下面的示例中,第三个评估返回false,一切都很好,但第四个示例返回true ..
我不太明白这是如何工作的,但默认情况下Object.Equals
比较两个对象相等的引用,并看到a
和b
都指向一个字符串的唯一实例,这应该返回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
}
答案 0 :(得分:10)
线索是"默认情况下"。 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(一旦您掌握了它,这实际上非常简单)