从不调用覆盖相等运算符



这个类的方法

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);
}
}

编译到此 IL 代码中:

.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

目前为止,一切都好。但是,无论是ceq还是System.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);

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

然而,多态性确实有效。因此,您对object.Equals(object)override应该可以正常工作。但是:它使用object.Equals(x, y)(静态方法) - 它会在调用您的方法之前null进行较早的检查。它知道null和"非空"在语义上不相等。如果您不想这样做:不要使用静态object.Equals(x, y)

可以使用的静态Equals方法表示:

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

所以:(相同的引用,或两个空),或(两个不为空,x.Equals(y))

此实现避免了x.Equals(y)的异常实现可能具有Equals(a, b) // trueEquals(b, a) // false

最新更新