100

I've already gone through question

I understand that, it is necessary to implement ==, != and 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);
    }
}

I assume, I've written code properly to override ==,!=,Equals operators. Though, I get compilation errors as follows.

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

So, question is - How to override above operators & get rid of this error?

Community
  • 1
  • 1
sagarkothari
  • 24,520
  • 50
  • 165
  • 235
  • isn't the signature for Equals like `public override bool Equals(object o)`? – bansi Aug 23 '14 at 11:53
  • 2
    Resharper suggests writing `!=` like `return !(obj1 == obj2)` which should take advantage of what you already wrote for the `==` overload. – drzaus Jul 18 '17 at 19:44

4 Answers4

106

As Selman22 said, you are overriding the default object.Equals method, which accepts an object obj and not a safe compile time type.

In order for that to happen, make your type implement 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;
        }
    }
}

Another thing to note is that you are making a floating point comparison using the equality operator and you might experience a loss of precision.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • `public override bool Equals(object obj) => obj is Box && Equals(obj as Box);` – fubo Apr 03 '23 at 06:12
  • use: public override int GetHashCode() => Hashcode.Combine(height, length, breadth); instead of the "unchecked" section to simplify the code – DevLocus May 24 '23 at 19:09
48

I think you declared the Equals method like this:

public override bool Equals(BOX obj)

Since the object.Equals method takes an object, there is no method to override with this signature. You have to override it like this:

public override bool Equals(object obj)

If you want type-safe Equals, you can implement IEquatable<BOX>.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
43

In fact, this is a "how to" subject. So, here is the reference implementation:

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

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

UPDATE: the cast to (object) in the operator == implementation is important, otherwise, it would re-execute the operator == overload, leading to a stackoverflow. Credits to @grek40.

This (object) cast trick is from Microsoft String == implementaiton. SRC: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs#L643

epox
  • 9,236
  • 1
  • 55
  • 38
  • see also: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs – epox May 03 '17 at 18:11
  • 22
    Don't write `if (null == b1)`, it will re-execute the `operator ==` overload, leading to a stackoverflow. Same for `b2`. – grek40 Oct 20 '17 at 09:42
  • 2
    @grek40 , will `if ((object)b1==null)` avoid the issue? – epox Oct 22 '17 at 21:46
  • 5
    Yes, this should do the trick. Alternative would be using `object.ReferenceEquals` like the other answer. – grek40 Oct 23 '17 at 05:27
  • I think ```(object)b1==null``` could return a _NullObjectException_ or _InvalidCastException_ – Alex 75 Apr 07 '19 at 13:05
  • 4
    For HashCode generation, it is better to use a tupple.GetHashCode(): `(height, length, breadth).GetHashCode()`. The problem with using XOR is that height, length, breadth probably use only the lower bits and the higher bits are unused. A hash ideally uses all 32 bits of the integer. This is achieved by bit-shifting the parameters by different values, which GetHashCode() does. – Peter Huber Apr 14 '19 at 08:38
6
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();
    }
}
Marko Pavic
  • 61
  • 1
  • 1