6

我已经使用以下代码在一个类中实现了 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);
}
4

8 回答 8

5

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

来自重载 Equals() 和运算符 == 的指南(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 a.x == b.x && a.y == b.y && a.z == b.z;
}

public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
    return !(a == b);
}
于 2009-06-09T22:25:59.390 回答
2

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

public class ClauseBE : IEquatable<ClauseBE>
{
    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);
    }
}
于 2009-06-09T22:54:14.980 回答
1

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

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

首先,在你的==运营商开始你有这个:

    // 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()测试,你就会知道这一点a并且b与众不同。

所以你的下一个测试:

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

可以简化 - 因为您知道 ifa为 null,b不能为 null,等等。

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

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

    // Use the implementation of Equals() for the rest
    return a.Equals(b as object);
于 2009-06-10T03:27:28.687 回答
1

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

于 2009-06-09T22:26:20.420 回答
1

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

ReferenceEquals(a, null)
于 2009-06-09T22:41:15.943 回答
0

我使用了以下方法,它似乎对我很有效。事实上,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);
}
于 2009-06-10T02:38:23.013 回答
0
public class Foo : IEquatable<Foo>
{
    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);
    }
}
于 2009-06-09T22:54:07.727 回答
0

我更喜欢在 Equals(T) 方法中执行所有比较逻辑,并将运算符重载中的“如果这个或那个为空,否则……”留给框架。

覆盖运算符重载的唯一棘手之处在于,您不能再在 Equals 实现中使用这些运算符,例如与null. 相反,object.ReferenceEquals可以用来达到同样的效果。

遵循 MSDN Guidelines for Overriding Equals() and 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<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;
}

在比较引用类型或装箱值类型无关紧要时,您还可以EqualityComparer<T>将运算符重载中的调用替换为对静态方法的调用: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.

于 2009-07-19T14:08:01.543 回答