C# IEquatable接口检查null时应执行的操作

C# IEquatable接口检查null时应执行的操作,c#,iequatable,C#,Iequatable,我用以下代码在一个类中实现了IEquatable接口 public bool Equals(ClauseBE other) { if (this._id == other._id) { return true; } return false; } public override bool Equals(O

我用以下代码在一个类中实现了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);
        }
这段代码在大多数情况下都能很好地工作。但是,以下检查会在相等运算符重载方法中引发异常,因为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”作为参数之一重写重载运算符的调用


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

我认为这比在检查null之前强制转换到Object要简单一些:

ReferenceEquals(a, null)
公共类Foo:IEquatable
{
公共Int32 Id{get;set;}
公共重写Int32 GetHashCode()
{
返回此.Id.GetHashCode();
}
公共覆盖布尔等于(对象obj)
{
return!Object.ReferenceEquals(obj作为Foo,null)
&&(this.Id==((Foo)obj.Id);
//要使用==运算符的对象的可选强制转换。
返回((Object)(obj作为Foo)!=null)&(this.Id==((Foo)obj.Id);
}
公共静态布尔运算符==(Foo a,Foo b)
{
返回对象。等于(a,b);
}
公共静态布尔运算符!=(Foo a,Foo b)
{
return!Object.等于(a,b);
}
公共布尔等于(Foo其他)
{
返回Object.Equals(this,other);
}
}

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

公共类条款b:i可满足
{
私人内部id;
公共布尔等于(其他条款)
{
if(ReferenceEquals(null,其他))
返回false;
if(ReferenceEquals(this,other))
返回true;
返回其他。_id==此。_id;
}
公共覆盖布尔等于(对象对象对象)
{
if(ReferenceEquals(null,obj))
返回false;
if(ReferenceEquals(this,obj))
返回true;
if(obj.GetType()!=typeof(ClauseBE))
返回false;
返回等于((条款B)obj);
}
公共覆盖int GetHashCode()
{
返回此值。_id.GetHashCode();
}
公共静态布尔运算符==(克劳塞贝左,克劳塞贝右)
{
返回等于(左、右);
}
公共静态布尔运算符!=(克劳塞贝左,克劳塞贝右)
{
返回!等于(左,右);
}
}

我使用了以下方法,它似乎对我很有效。事实上,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);
}

其他答案很好地解决了一般问题

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

首先,在
==
操作符的开头,您有以下内容:

    // 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;
    }
另外,这还可以处理
a
b
是同一个精确对象的情况

一旦通过
对象.ReferenceEquals()
测试,您就知道
a
b
是不同的

因此,您的下一个测试:

    // Second test
    if ((a as object == null && b as object != null)
        || (b as object == null && a as object != null))
    {
        return false;
    }
可以简化-因为您知道如果
a
为空,
b
不能为空,依此类推

    // Second test
    if (Object.Equals(a, null) || Object.Equals(b, null))
    {
        return false;
    }
如果此测试失败,则您知道
a
b
是不同的,并且两者都不为空。调用重写的
Equals()
是一个很好的时机


我更喜欢在Equals(T)方法中执行所有比较逻辑,并将操作符重载中的“if this或thas为null,else…”留给框架

重写运算符重载唯一棘手的事情是,您不能再在Equals实现中使用这些运算符,例如与
null
进行比较。相反,可以用来实现相同的效果

遵循MSDN文章中的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<int>.Default.Equals( this.X, other.X )
      && EqualityComparer<int>.Default.Equals( this.Y, other.Y );
}

public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
  // System.Collections.Generic.EqualityComparer<T> will perform the null checks 
  //  on the operands, and will call the Equals overload if necessary.
  return EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}

public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
  return !EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}
在比较引用类型或装箱值类型无关紧要时,也可以将运算符重载中的调用替换为对静态方法的调用:

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

以==,作为回报!(a==b);您可能还想检查一下:我实际上是从IEquatable.Equals方法文档的示例开始的,当a和b都为null时,这将返回一个与ReferenceEquals不一致的值。但是你是对的,这当然是微软的建议。“System.Object”在ReferenceEquals前面有点多余-你可以在继承自System.Object的任何类中访问该方法,而无需限定。这就是他们的全部,完全同意,这是逐字逐句
    // Second test
    if (Object.Equals(a, null) || Object.Equals(b, null))
    {
        return false;
    }
    // Use the implementation of Equals() for the rest
    return a.Equals(b as object);
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<int>.Default.Equals( this.X, other.X )
      && EqualityComparer<int>.Default.Equals( this.Y, other.Y );
}

public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
  // System.Collections.Generic.EqualityComparer<T> will perform the null checks 
  //  on the operands, and will call the Equals overload if necessary.
  return EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}

public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
  return !EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}
public bool Equals( TwoDPoint other ) {
  return !object.ReferenceEquals( other, null )
      && this.X == other.X
      && this.Y == other.Y;
}
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 );
}