永远不会调用覆盖相等运算符
Overriden equality operator is never called
这个方法class
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);
}
}
.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_Equality
或 Object.Equals
。
这是为什么?为什么建议的三种方法中的 none 调用 operator==
或重写的 Equals
方法? System.Object::Equals(object, object)
不应该自动调用任何重写的 Equals
方法吗?
编辑:
我用于测试目的的 class 如下所示:
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);
泛型的要点是:它们不是 "templates"。对于所有 T
,完全相同的 IL 需要 运行。这意味着,由于您的示例中 T
没有任何限制,IL 中唯一已知的运算符是 object
存在的运算符,因此 ==
表示引用相等,与 (object)x == (object)y
.
相同
多态性,但是,确实有效。因此,您在 object.Equals(object)
上的 override
应该可以正常工作。但是:如果您使用 object.Equals(x, y)
(静态方法)- 它会在 earlier 检查 null
、before 它调用你的方法。它 知道 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)));
所以:(相同的引用,或均为空),或(均不为空,且x.Equals(y)
)
此实现避免了 x.Equals(y)
的异常实现可能具有 Equals(a, b) // true
但 Equals(b, a) // false
之类的问题
这个方法class
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);
}
}
.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_Equality
或 Object.Equals
。
这是为什么?为什么建议的三种方法中的 none 调用 operator==
或重写的 Equals
方法? System.Object::Equals(object, object)
不应该自动调用任何重写的 Equals
方法吗?
编辑: 我用于测试目的的 class 如下所示:
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);
泛型的要点是:它们不是 "templates"。对于所有 T
,完全相同的 IL 需要 运行。这意味着,由于您的示例中 T
没有任何限制,IL 中唯一已知的运算符是 object
存在的运算符,因此 ==
表示引用相等,与 (object)x == (object)y
.
多态性,但是,确实有效。因此,您在 object.Equals(object)
上的 override
应该可以正常工作。但是:如果您使用 object.Equals(x, y)
(静态方法)- 它会在 earlier 检查 null
、before 它调用你的方法。它 知道 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)));
所以:(相同的引用,或均为空),或(均不为空,且x.Equals(y)
)
此实现避免了 x.Equals(y)
的异常实现可能具有 Equals(a, b) // true
但 Equals(b, a) // false