永远不会调用Overriden相等运算符

时间:2018-03-28 07:46:56

标签: c# operator-overloading cil

本课程的方法

public class NullTester
{
    public bool EqualsNull<T>(T o) where T : class
    {
        return o == null;
    }

    public bool IsNull<T>(T o) where T : class
    {
        return o is null;
    }

    public bool EqualsCall<T>(T o) where T : class
    {
        return object.Equals(o, null);
    }
}

compile into this IL code:

.method public hidebysig 
    instance bool EqualsNull<class T> (
        !!T o
    ) cil managed 
{
    .maxstack 8

    IL_0000: ldarg.1
    IL_0001: box !!T
    IL_0006: ldnull
    IL_0007: ceq                                                          // IMPORTANT
    IL_0009: ret
} // end of method C::EqualsNull

.method public hidebysig 
    instance bool IsNull<class T> (
        !!T o
    ) cil managed 
{
    .maxstack 8

    IL_0000: ldarg.1
    IL_0001: box !!T
    IL_0006: ldnull
    IL_0007: ceq                                                          // IMPORTANT
    IL_0009: ret
} // end of method C::IsNull

.method public hidebysig 
    instance bool EqualsCall<class T> (
        !!T o
    ) cil managed 
{
    .maxstack 8

    IL_0000: ldarg.1
    IL_0001: box !!T
    IL_0006: ldnull
    IL_0007: call bool [mscorlib]System.Object::Equals(object, object)   // IMPORTANT
    IL_000c: ret
} // end of method C::EqualsCall

到目前为止,这么好。 ceqSystem.Object::Equals(object, object)都未考虑可能被覆盖的op_EqualityObject.Equals

为什么?为什么三种提议的方法都没有调用operator==或覆盖Equals方法? System.Object::Equals(object, object)不应自动调用任何被覆盖的Equals方法吗?

修改 我用于测试目的的类看起来像这样:

public class MyClass
{
     public static bool operator ==(MyClass m1, MyClass m2) => throw new Exception();

     public static bool operator !=(MyClass m1, MyClass m2) => throw new Exception();

     public override bool Equals(object obj) => throw new Exception();
}

以下三种方法都没有调用任何MyClass的覆盖成员:

NullTester tester = new NullTester();
MyClass myClass = new MyClass(); 

tester.IsNull(myClass);
tester.EqualsNull(myClass);
tester.EqualsCall(myClass);

1 个答案:

答案 0 :(得分:7)

泛型的要点是:它们不是“模板”。需要为所有T运行完全相同的IL。这意味着,由于示例中T没有约束,因此IL中已知的唯一运算符是object存在的运算符,因此==表示引用相等,与(object)x == (object)y相同。

然而,

多态性,确实有效。因此,override上的object.Equals(object)应该可以正常使用。但是:你使用object.Equals(x, y)(静态方法) - 在调用你的方法之前检查null之前做了之前的检查。 知道 null和“not null”在语义上不等于。如果您不希望这样:请勿使用静态object.Equals(x, y)

您正在使用的静态Equals方法可以表达:

public static bool Equals(object objA, object objB) => 
    ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));

所以:(同一个引用,或者都是null),或者(都不为null,x.Equals(y)

此实施可避免x.Equals(y)的异常实施可能包含Equals(a, b) // trueEquals(b, a) // false

等问题