为什么方法Equals只调用一次进行两次比较?

时间:2014-08-07 13:59:34

标签: c# equals

考虑以下简单程序。

using System;


namespace CompareClasses
{

    class A
    {
        public override bool Equals(object obj)
        {
            Console.WriteLine("\nHi, It is me A!");
            return base.Equals(obj);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a1 = new A();
            A a2 = new A();
            A a3 = a1;

            Console.WriteLine("compare a1 to a2: {0}", Equals(a1, a2));
            Console.WriteLine("compare a1 to a3: {0}", Equals(a1, a3));
        }
    }
}

其输出如下

Hi, It is me A!
compare a1 to a2: False
compare a1 to a3: True

所以问题是为什么消息嗨,这是我!只显示一次?

虽然如果要查看MSIL代码,我们可以看到静态方法Equals被调用两次。

  IL_000f:  ldstr      "compare a1 to a2: {0}"
  IL_0014:  ldloc.0
  IL_0015:  ldloc.1
  IL_0016:  call       bool [mscorlib]System.Object::Equals(object,
                                                            object)
  IL_001b:  box        [mscorlib]System.Boolean
  IL_0020:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_0025:  nop
  IL_0026:  ldstr      "compare a1 to a3: {0}"
  IL_002b:  ldloc.0
  IL_002c:  ldloc.2
  IL_002d:  call       bool [mscorlib]System.Object::Equals(object,
                                                            object)
  IL_0032:  box        [mscorlib]System.Boolean
  IL_0037:  call       void [mscorlib]System.Console::WriteLine(string,

3 个答案:

答案 0 :(得分:5)

因为object.Equals(obj1, obj2)将首先使用object.ReferenceEquals来检查两个对象是否都是相同的引用。这对于a1和a3都是如此。

来自MSDN:

  

静态Equals(Object,Object)方法指示是否两个   对象,objA和objB是相等的。它还使您可以测试对象   其值为null,表示相等。它比较了objA和objB   平等如下:

  1. 确定两个对象是否表示相同的对象引用。如果是,则该方法返回true。此测试等同于调用ReferenceEquals方法。另外
  2. 如果objAobjB都是null,则该方法返回true。它确定objAobjBnull。如果是这样,则返回false。如果两个对象不表示相同的对象引用,并且都不为null,则
  3. 调用objA.Equals(objB)并返回结果。这意味着如果objA覆盖Object.Equals(Object)方法,则会调用此覆盖。
  4. 这就是为什么只有这会调用你的覆盖Equals

    Console.WriteLine("compare a1 to a2: {0}", Equals(a1, a2));
    

    因为两个对象都是不同的引用,并且两者都不为空。

答案 1 :(得分:3)

您正在调用Program.Equals(object, object)(由于Program不会覆盖隐式Equals基类的object,因此会有效调用System.Object.Equals。它有一个早期退出参考等同物:

if(ReferenceEquals(o1, o2)) { return true; }
else return o1.Equals(o2); // only here get's your override bool Equals(object other) called

(与上面类似。显然也会检查空值)

答案 2 :(得分:1)

您正在调用Object上的Equals方法,它接受两个参数。以下可以看出反编译:

public static bool Equals(Object objA, Object objB) 
    {
        if (objA==objB) {
            return true;
        } 
        if (objA==null || objB==null) {
            return false; 
        } 
        return objA.Equals(objB);
    } 

第一次在比较a1和a2时调用此方法时,您使用的是两个不同的A实例。因此,您的方法将在返回objA.Equals(objB)上面的最后一行调用。

第二次调用此方法时,您将传递相同的A实例。将a3设置为等于a1。因此,if(objA == objB)行将为true,因此它只会在该点处快捷方式并返回true,并且永远不会调用被覆盖的方法。