IEquatable接口检查null时要做什么

我已经使用以下代码在类中实现了IEquatable接口。

public bool Equals(ClauseBE other) { if (this._id == other._id) { return true; } return false; } public override bool Equals(Object obj) { if (obj == null) { return base.Equals(obj); } if (!(obj is ClauseBE)) { throw new InvalidCastException("The 'obj' argument is not a ClauseBE object."); } return Equals(obj as ClauseBE); } public override int GetHashCode() { return this._id.GetHashCode(); } public static bool operator ==(ClauseBE a, ClauseBE b) { // cast to object so we call the overloaded Equals function which appropriately checks when b is null. return a.Equals(b as object); } public static bool operator !=(ClauseBE a, ClauseBE b) { // cast to object so we call the overloaded Equals function which appropriately checks when b is null. return !a.Equals(b as object); } 

此代码适用于大多数情况。 但是,以下检查会在等于运算符重载方法中引发exception,因为a为null,因此没有Equals方法。

 if(this.Clause != null) { } 

解决此问题的标准方法是什么?

编辑

我已经去过这个,但看起来很麻烦。 我希望有一种更优雅的方式来实现这一目标。

  public static bool operator ==(ClauseBE a, ClauseBE b) { if (a as object == null && b as object == null) { return true; } if ((a as object == null && b as object != null) || (b as object == null && a as object != null)) { return false; } // cast to object so we call the overloaded Equals function which appropriately checks when b is null. return a.Equals(b as object); } public static bool operator !=(ClauseBE a, ClauseBE b) { if (a as object == null && b as object == null) { return false; } if((a as object == null && b as object != null) || (b as object == null && a as object != null)) { return true; } // cast to object so we call the overloaded Equals function which appropriately checks when b is null. return !a.Equals(b as object); } 

谢谢大家。 我从每个人那里得到了很多好的建议,我真的很感激。 这是我最终确定的,它比我开始时更优雅。 除运算符重载外,所有代码都相同。

 public static bool operator ==(ClauseBE a, ClauseBE b) { if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) { return true; } if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) { return false; } return a.Equals(b); } public static bool operator !=(ClauseBE a, ClauseBE b) { return !(a == b); } 

我总是发现使用null处理编写静态运算符更容易,并使Equals覆盖调用重载运算符,并将“this”作为参数之一。

来自重载等于()和运算符的指南==(C#编程指南)

 //add this code to class ThreeDPoint as defined previously // public static bool operator ==(ThreeDPoint a, ThreeDPoint b) { // If both are null, or both are same instance, return true. if (System.Object.ReferenceEquals(a, b)) { return true; } // If one is null, but not both, return false. if (((object)a == null) || ((object)b == null)) { return false; } // Return true if the fields match: return ax == bx && ay == by && az == bz; } public static bool operator !=(ThreeDPoint a, ThreeDPoint b) { return !(a == b); } 

这就是ReSharper如何创建相等运算符并实现IEquatable ,我当然盲目相信;-)

 public class ClauseBE : IEquatable { private int _id; public bool Equals(ClauseBE other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return other._id == this._id; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != typeof(ClauseBE)) return false; return Equals((ClauseBE)obj); } public override int GetHashCode() { return this._id.GetHashCode(); } public static bool operator ==(ClauseBE left, ClauseBE right) { return Equals(left, right); } public static bool operator !=(ClauseBE left, ClauseBE right) { return !Equals(left, right); } } 

检查null并返回false。 如果其中一个操作数为null,则Equals应始终为false;

我认为这比在检查null之前转换为Object要麻烦一点:

 ReferenceEquals(a, null) 

其他答案为一般问题提供了很好的解决方案。

但是,您自己的代码可以简化为一个相对简单的解决方案……

首先,在您的==运算符的开头,您有:

  // First test if (a as object == null && b as object == null) { return true; } 

这有资格“工作太辛苦”。

如果ClauseBE是一个引用类型,那么你只需要与null进行比较 – “ as object ”是多余的; 同样,如果ClauseBE是一个值类型,那么它永远不能为null

假设ClauseBE是一个引用类型(最可能的情况),那么你可以简化到这一点 – 注意我们使用Object.Equals()来避免无限递归和堆栈井喷。

  // First test if (Object.Equals(a, null) && Object.Equals(b, null)) { return true; } 

一个有用的快捷方式是使用Object.ReferenceEquals() – 它为您处理空值。

所以你可以这样写:

  // First test if (Object.ReferenceEquals(a, b)) { return true; } 

有奖金,这也处理ab是同一个确切对象的情况。

一旦超过了Object.ReferenceEquals()测试,就会知道ab是不同的。

所以你的下一个测试:

  // Second test if ((a as object == null && b as object != null) || (b as object == null && a as object != null)) { return false; } 

可以简化 – 因为你知道如果a为null,则b不能为null,依此类推。

  // Second test if (Object.Equals(a, null) || Object.Equals(b, null)) { return false; } 

如果此测试失败,那么您知道ab不同,并且两者都不为null。 是调用被覆盖的Equals()好时机。

  // Use the implementation of Equals() for the rest return a.Equals(b as object); 
 public class Foo : IEquatable { public Int32 Id { get; set; } public override Int32 GetHashCode() { return this.Id.GetHashCode(); } public override Boolean Equals(Object obj) { return !Object.ReferenceEquals(obj as Foo, null) && (this.Id == ((Foo)obj).Id); // Alternative casting to Object to use == operator. return ((Object)(obj as Foo) != null) && (this.Id == ((Foo)obj).Id); } public static Boolean operator ==(Foo a, Foo b) { return Object.Equals(a, b); } public static Boolean operator !=(Foo a, Foo b) { return !Object.Equals(a, b); } public Boolean Equals(Foo other) { return Object.Equals(this, other); } } 

我使用了以下方法,它似乎对我有用。 事实上,Resharper建议采用这种方法。

 public bool Equals(Foo pFoo) { if (pFoo == null) return false; return (pFoo.Id == Id); } public override bool Equals(object obj) { if (ReferenceEquals(obj, this)) return true; return Equals(obj as Foo); } 

我更喜欢在Equals(T)方法中执行所有比较逻辑,并在运算符重载到框架中留下“if this或that null,else ……”。

覆盖运算符重载的唯一棘手的问题是,您不能再在Equals实现中使用这些运算符,例如与null进行比较。 相反, object.ReferenceEquals可用于实现相同的效果。

遵循MSDN 指南中的OverDPiding Equals()和Operator ==文章中的TwoDPoint示例,这是我在为类型实现值相等时生成的模式:

 public override bool Equals( object obj ) { // Note: For value types, would use: // return obj is TwoDPoint && this.Equals( (TwoDPoint)obj ); return this.Equals( obj as TwoDPoint ); } public bool Equals( TwoDPoint other ) { // Note: null check not needed for value types. return !object.ReferenceEquals( other, null ) && EqualityComparer.Default.Equals( this.X, other.X ) && EqualityComparer.Default.Equals( this.Y, other.Y ); } public static bool operator ==( TwoDPoint left, TwoDPoint right ) { // System.Collections.Generic.EqualityComparer will perform the null checks // on the operands, and will call the Equals overload if necessary. return EqualityComparer.Default.Equals( left, right ); } public static bool operator !=( TwoDPoint left, TwoDPoint right ) { return !EqualityComparer.Default.Equals( left, right ); } 

上面的表单是最安全的实现,因为它只是将字段相等性检查转发到框架,并且不需要知道字段是否重载相等运算符。 在您知道存在过载的情况下简化此操作是完全可以的:

 public bool Equals( TwoDPoint other ) { return !object.ReferenceEquals( other, null ) && this.X == other.X && this.Y == other.Y; } 

您还可以在运算符重载时替换EqualityComparer调用,并在比较引用类型时调用静态object.Equals方法,或者在装箱值类型无关紧要时:

 public static bool operator ==( TwoDPoint left, TwoDPoint right ) { return object.Equals( left, right ); } public static bool operator !=( TwoDPoint left, TwoDPoint right ) { return !object.Equals( left, right ); } 

另请参阅重写的GetHashCode的最佳算法是什么? 用于实现GetHashCode