重载运算符 ==、!=、Equals

105

我已经阅读过这个问题

我理解,实现==!=Equals()是必要的。

public class BOX
{
    double height, length, breadth;

    // this is first one '=='
    public static bool operator== (BOX obj1, BOX obj2)
    {
        return (obj1.length == obj2.length 
                    && obj1.breadth == obj2.breadth 
                    && obj1.height == obj2.height);
    }

    // this is second one '!='
    public static bool operator!= (BOX obj1, BOX obj2)
    {
        return !(obj1.length == obj2.length 
                    && obj1.breadth == obj2.breadth 
                    && obj1.height == obj2.height);
    }

    // this is third one 'Equals'
    public override bool Equals(BOX obj)
    {
        return (length == obj.length 
                    && breadth == obj.breadth 
                    && height == obj.height);
    }
}

我假设我已经正确编写了代码来覆盖==!=Equals运算符。但是,我会得到以下编译错误。

'myNameSpace.BOX.Equals(myNameSpace.BOX)' is marked as an override 
but no suitable method found to override.

那么,问题是 - 如何重载上述运算符并摆脱此错误?


йҡҫйҒ“Equalsж–№жі•зҡ„зӯҫеҗҚдёҚжҳҜpublic override bool Equals(object o)еҗ—пјҹ - bansi
2
Resharper建议将!=写成return !(obj1 == obj2)的形式,这样就可以利用已经为==重载所写的内容。 - drzaus
4个回答

110

正如Selman22所说,您正在覆盖默认的object.Equals方法,该方法接受一个object obj而不是安全的编译时类型。

为了实现这一点,请使您的类型实现IEquatable<Box>

public class Box : IEquatable<Box>
{
    double height, length, breadth;

    public static bool operator ==(Box obj1, Box obj2)
    {
        if (ReferenceEquals(obj1, obj2)) 
            return true;
        if (ReferenceEquals(obj1, null)) 
            return false;
        if (ReferenceEquals(obj2, null))
            return false;
        return obj1.Equals(obj2);
    }
    public static bool operator !=(Box obj1, Box obj2) => !(obj1 == obj2);
    public bool Equals(Box other)
    {
        if (ReferenceEquals(other, null))
            return false;
        if (ReferenceEquals(this, other))
            return true;
        return height.Equals(other.height) 
               && length.Equals(other.length) 
               && breadth.Equals(other.breadth);
    }
    public override bool Equals(object obj) => Equals(obj as Box);

    public override int GetHashCode()
    {
        unchecked
        {
            int hashCode = height.GetHashCode();
            hashCode = (hashCode * 397) ^ length.GetHashCode();
            hashCode = (hashCode * 397) ^ breadth.GetHashCode();
            return hashCode;
        }
    }
}

另外需要注意的是,您正在使用等号运算符进行浮点数比较,可能会导致精度损失。


public override bool Equals(object obj) => obj is Box && Equals(obj as Box); - fubo
使用以下代码替换"unchecked"部分以简化代码:public override int GetHashCode() => Hashcode.Combine(height, length, breadth); - DevLocus
关于“精度损失”的注释:如果您决定实现一个模糊的Equals方法,考虑到精度损失,并声明两个盒子在某个epsilon范围内相等,那么您将违反GetHashCode的约定:GetHashCode不应该对相等的对象返回不同的结果。 - undefined

50

我认为你声明了Equals方法,像这样:

public override bool Equals(BOX obj)

由于object.Equals方法需要一个对象作为参数,因此没有可以覆盖此签名的方法。您必须像这样重写它:

由于object.Equals方法需要一个对象作为参数,因此没有可以覆盖此签名的方法。您必须像这样进行重写:

public override bool Equals(object obj)

如果您想要类型安全的Equals,您可以实现IEquatable<BOX>


44

实际上,这是一个“如何”主题。因此,这里是参考实现:

    public class BOX
    {
        double height, length, breadth;

        public static bool operator == (BOX b1, BOX b2)
        {
            if ((object)b1 == null)
                return (object)b2 == null;

            return b1.Equals(b2);
        }

        public static bool operator != (BOX b1, BOX b2)
        {
            return !(b1 == b2);
        }

        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
                return false;

            var b2 = (BOX)obj;
            return (length == b2.length && breadth == b2.breadth && height == b2.height);
        }

        public override int GetHashCode()
        {
            return height.GetHashCode() ^ length.GetHashCode() ^ breadth.GetHashCode();
        }
    }

参考文献: https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx#Examples

更新:在operator ==实现中的(object)转换很重要,否则会重新执行操作符重载,导致堆栈溢出。感谢@grek40提供的帮助。

这个(object)转换的技巧来自于 Microsoft 的 String == 实现。 源代码: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs#L643


请参见:https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs - epox
22
不要写成 if (null == b1),这会重新执行 operator == 重载运算符,可能导致堆栈溢出。b2 同理。 - grek40
2
@grek40,if ((object)b1==null) 这样写能避免问题吗? - epox
5
可以,这样做就可以了。另一种选择是使用像其他答案中所提到的 object.ReferenceEquals - grek40
我认为 (object)b1==null 可能会返回 NullObjectException 或 _InvalidCastException_。 - Alex 75
4
为了生成HashCode,最好使用元组的GetHashCode()方法:(height, length, breadth).GetHashCode()。使用异或运算符的问题在于,高位可能未被使用,因为height、length和breadth可能只使用低位。哈希理想情况下应该使用整数的所有32位。通过使用不同的值对参数进行位移来实现这一点,这正是GetHashCode()所做的。 - Peter Huber

8
public class BOX
{
    double height, length, breadth;

    public static bool operator == (BOX b1, BOX b2)
    {
        if (b1 is null)
            return b2 is null;

        return b1.Equals(b2);
    }

    public static bool operator != (BOX b1, BOX b2)
    {
        return !(b1 == b2);
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        return obj is BOX b2? (length == b2.length && 
                               breadth == b2.breadth && 
                               height == b2.height): false;

    }

    public override int GetHashCode()
    {
        return (height,length,breadth).GetHashCode();
    }
}

使用 is 而不是 == 可以消除将类型转换为对象的解决方法。 - liviriniu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接