代码之家  ›  专栏  ›  技术社区  ›  Matthew Vines

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

  •  6
  • Matthew Vines  · 技术社区  · 15 年前

            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);
    }
    
    8 回复  |  直到 11 年前
        1
  •  5
  •   micahtan    15 年前

    我总是发现编写带有null处理的静态运算符更容易,并且让Equals用“this”作为参数之一重写重载运算符的调用。

    从…起 Guidelines for Overloading Equals() and Operator == (C# Programming Guide)

    //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);
    }
    
        2
  •  2
  •   Lucas    15 年前

    这就是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);
        }
    }
    
        3
  •  1
  •   Robert    15 年前

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

        4
  •  1
  •   Joel Mueller    15 年前

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

    ReferenceEquals(a, null)
    
        5
  •  1
  •   Bevan    15 年前

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

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

    首先,在你开始工作的时候 ==

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

    这被称为“工作太辛苦”。

    如果 ClauseBE 是引用类型,则只需与 null as object 是一个值类型,那么它永远不能是 无效的 .

    假设 克劳塞贝 是一个引用类型(最有可能的情况),那么您可以简化为这个-注意我们使用 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 都是同一个物体。

    一旦你通过了考试 A. B 它们是不同的。

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

    可以简化-因为你知道如果 A. 不能为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);
    
        6
  •  0
  •   Daniel Brückner Pradip    15 年前
    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);
        }
    }
    
        7
  •  0
  •   Vasu Balakrishnan    15 年前

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

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

    重写运算符重载唯一棘手的事情是,您不能再在Equals实现中使用这些运算符,例如与 null object.ReferenceEquals 可以用来达到同样的效果。

    遵循MSDN中的双点示例 Guidelines for Overriding Equals() and Operator == 本文中,这是我在实现类型的值相等时生成的模式:

    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 );
    }
    

    另见 What is the best algorithm for an overridden GetHashCode? 实施 GetHashCode