如何在没有无限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 == nullObject.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)方法指示两个对象objAobjB是否相等。 它还使您能够testing值相同的对象。 它比较objAobjB的平等如下:

  • 它确定两个对象是否代表相同的对象引用。 如果他们这样做,该方法返回true 。 这个testing等价于调用ReferenceEquals方法。 另外,如果objAobjB都为null ,则方法返回true
  • 它确定objAobjB是否为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; }