如何在没有无限recursion的情况下检查'=='运算符超载的空值?
以下将导致==运算符重载方法的无限recursion
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); }
使用ReferenceEquals
。 从MSDN论坛 :
public static bool operator ==(Foo foo1, Foo foo2) { if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null); if (ReferenceEquals(foo2, null)) return false; return foo1.field1 == foo2.field2; }
尝试Object.ReferenceEquals(foo1, null)
无论如何,我不会推荐重载==
操作符; 它应该用于比较引用,并使用Equals
进行“语义”比较。
如果我已经覆盖了bool Equals(object obj)
,我希望operator ==
和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)
,如果二者相等,我已经重写了它来做实际的检查。
我的做法是做
(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; }
该约束可以防止值types的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)
速度要Object.ReferenceEquals(item, null)
,但是只有当它重要的时候(我正在做一些我需要微观优化的东西! )。
要查看有关实现平等检查的完整指南,请参阅比较两个引用types的实例的“最佳实践”是什么?
如果您使用的是C#7或更高版本,则可以使用空常量模式匹配:
public static bool operator==(Foo foo1, Foo foo2) { if (foo1 is null) return foo2 is null; return foo1.Equals(foo2); }
这给你一个比一个调用对象稍微整洁的代码.ReferenceEquals(foo1,null)
静态Equals(Object, Object)
方法指示两个对象objA
和objB
是否相等。 它还使您能够testing值相同的对象。 它比较objA
和objB
的平等如下:
- 它确定两个对象是否代表相同的对象引用。 如果他们这样做,该方法返回
true
。 这个testing等价于调用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); }
运算符==的重载常见错误是使用
(a == b)
,(a ==null)
或(b == null)
来检查引用是否相等。 这会导致对重载运算符==的调用,导致infinite loop
。 使用ReferenceEquals
或将types转换为Object,以避免循环。
看看这个
// 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; }
参考指南重载Equals()和运算符==
您可以尝试使用对象属性并捕获NullReferenceException。 如果您尝试的属性是从Objectinheritance或重写的,那么这适用于任何类。
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; }