如何在没有无限递归的情况下检查“==”运算符重载中的空值?
-
09-06-2019 - |
题
以下将导致 == 运算符重载方法无限递归
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (foo1 == null) return foo2 == null;
return foo1.Equals(foo2);
}
如何检查空值?
解决方案
使用 ReferenceEquals
:
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (object.ReferenceEquals(null, foo1))
return object.ReferenceEquals(null, foo2);
return foo1.Equals(foo2);
}
其他提示
在重载方法中强制转换为对象:
public static bool operator ==(Foo foo1, Foo foo2) {
if ((object) foo1 == null) return (object) foo2 == null;
return foo1.Equals(foo2);
}
尝试 Object.ReferenceEquals(foo1, null)
无论如何,我不建议超载 ==
操作员;它应该用于比较参考,并使用 Equals
用于“语义”比较。
如果我已经覆盖了 bool Equals(object obj)
我想要接线员 ==
和 Foo.Equals(object obj)
为了返回相同的答案,我通常会执行 !=
像这样的运算符:
public static bool operator ==(Foo foo1, Foo foo2) {
return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
return !object.Equals(foo1, foo2);
}
运营商 ==
然后在为我完成所有空检查后最终会调用 foo1.Equals(foo2)
我已重写以进行实际检查两者是否相等。
如果您使用 C# 7 或更高版本,则可以使用空常量模式匹配:
public static bool operator==(Foo foo1, Foo foo2)
{
if (foo1 is null)
return foo2 is null;
return foo1.Equals(foo2);
}
这为您提供了比调用 object.ReferenceEquals(foo1, null) 的代码稍微简洁的代码
我的方法是做
(object)item == null
我所依赖的 object
自己的相等运算符不会出错。或者自定义扩展方法(和重载):
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null;
}
public static bool IsNull<T>(this T? obj) where T : struct
{
return !obj.HasValue;
}
或者处理更多情况,可能是:
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
该约束阻止 IsNull
关于值类型。现在就像打电话一样甜蜜
object obj = new object();
Guid? guid = null;
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error
这意味着我有一种一致/不易出错的方式来检查空值。我也发现了 (object)item == null
比非常非常非常快一点 Object.ReferenceEquals(item, null)
, ,但前提是它很重要(我目前正在研究一些必须对所有内容进行微优化的事情!)。
要查看有关实施平等检查的完整指南,请参阅 比较引用类型的两个实例的“最佳实践”是什么?
静态的 Equals(Object, Object)
方法 指示是否有两个对象, objA
和 objB
, 相等。它还使您能够测试值为 null
为了平等。它比较 objA
和 objB
对于平等如下:
- 它确定两个对象是否表示相同的对象引用。如果这样做,该方法将返回
true
. 。这个测试相当于调用ReferenceEquals
方法。另外,如果两者objA
和objB
是null
, ,该方法返回true
. - 它决定是否
objA
或者objB
是null
. 。如果是,则返回false
。如果两个对象不代表相同的对象引用并且两者都不是null
, ,它调用objA.Equals(objB)
并返回结果。这意味着如果objA
覆盖Object.Equals(Object)
方法,调用此覆盖。
.
public static bool operator ==(Foo objA, Foo objB) {
return Object.Equals(objA, objB);
}
回复更多 重写运算符如何与 null 进行比较 重定向到这里作为重复项。
在这样做是为了支持值对象的情况下,我发现新的表示法很方便,并且希望确保只有一个地方进行比较。还利用 Object.Equals(A, B) 简化了空检查。
这将重载 ==、!=、Equals 和 GetHashCode
public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
public override bool Equals(object other) => Equals(other as ValueObject );
public bool Equals(ValueObject other) {
return !(other is null) &&
// Value comparisons
_value == other._value;
}
public override int GetHashCode() => _value.GetHashCode();
对于更复杂的对象,请在 Equals 和更丰富的 GetHashCode 中添加额外的比较。
实际上有一种更简单的方法来检查 null
在这种情况下:
if (foo is null)
就是这样!
此功能是在 C# 7 中引入的
运算符 == 重载中的一个常见错误是使用
(a == b)
,(a ==null)
, , 或者(b == null)
检查引用是否相等。这个代替 结果是 对重载运算符 == 的调用,导致infinite loop
. 。使用ReferenceEquals
或将类型施放到对象中,以避免循环。
看看这个
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
return false;
}
您可以尝试使用对象属性并捕获生成的 NullReferenceException。如果您尝试的属性是从 Object 继承或重写的,那么这适用于任何类。
public static bool operator ==(Foo foo1, Foo foo2)
{
// check if the left parameter is null
bool LeftNull = false;
try { Type temp = a_left.GetType(); }
catch { LeftNull = true; }
// check if the right parameter is null
bool RightNull = false;
try { Type temp = a_right.GetType(); }
catch { RightNull = true; }
// null checking results
if (LeftNull && RightNull) return true;
else if (LeftNull || RightNull) return false;
else return foo1.field1 == foo2.field2;
}