Implementing (IComparable<T>), IEquatable<T> and the equality members

IComparable<T>, IEquantable<T>, IHaveTheSolution

Reading through the web I hit upon two blog posts (Classes implementing IComparable<T> and null (German) and the following post Classes implementing IComparable<T> and null the second (German)). In the first one there was a discussion about how to implement the “equality members” (Equals, IEquatable<T> and the equality operators).

One problem was, that overriding the equality operator (==) so that it calls Equals makes an implementation of Equals as follows impossible (lets say our class is called Foo):

//== operator is overloaded so it calls Equals

public bool Equals(Foo other)
{
    if (foo == null)
        return false;

    //"Real" comparsion
}

There will occur a StackOverflowException as “foo == null” results in a further call of Equals.

Someone even suggested catching the NullReferenceException that will occur when accessing other if it is null.

I couldn’t help writing my own solution :-)

My solution

So here it is – a reference implementation that should be used in order to prevent all the problems that may occur.

public sealed class Foo : IEquatable<Foo>, IComparable<Foo>
{
    public static int Compare(Foo first, Foo second)
    {
        if (Object.ReferenceEquals(first, null))
            return (Object.ReferenceEquals(second, null) ? 0 : -1);

        return first.CompareTo(second);
    }
    public static bool operator==(Foo first, Foo second)
    {
        return Object.Equals(first, second);
    }
    public static bool operator!=(Foo first, Foo second)
    {
        return !Object.Equals(first, second);
    }
    public static bool operator<(Foo first, Foo second)
    {
        return Foo.Compare(first, second) < 0;
    }
    public static bool operator >(Foo first, Foo second)
    {
        return Foo.Compare(first, second) > 0;
    }
    public static bool operator <=(Foo first, Foo second)
    {
        return Foo.Compare(first, second) <= 0;
    }
    public static bool operator >=(Foo first, Foo second)
    {
        return Foo.Compare(first, second) >= 0;
    }

    private string bar;

    public string Bar
    {
        //getter and setter
    }

   public bool Equals(Foo other)
    {
        if (Object.ReferenceEquals(other, null))
            return false;
        if (Object.ReferenceEquals(other, this)) //Not mandatory
            return true;

        return String.Equals(this.foo, other.foo);
    }
    public int CompareTo(Foo other)
    {
        if (Object.ReferenceEquals(other, null))
            return 1;
        if (Object.ReferenceEquals(other, this)) //Not mandatory
            return 0;

        return String.Compare(this.bar, other.bar);
    }
    public override bool Equals(object obj)
    {
        return this.Equals(obj as Foo);
    }
    public override int GetHashCode()
    {
        return this.bar == null ? 0 : this.bar.GetHashCode();
    }
}

If you don’t want to compare two Foos, you can simple leave out the members for comparing.

Points of Interest

Using Object.ReferenceEquals

You will notice that at some points I used “Object.ReferenceEquals(foo, null)” instead of “foo == null”. This is because I overloaded the ==-operator. Otherwise the mentioned StackOverflowException would occur. Object.ReferenceEquals really checks if two references are identical.

Equality Operators

Do not forget that one (or both) parameters of an operator can be null. Therefore I used the static Object.Equals in the equality operators which is implemented as follows (regarding to the .NET Reflector):

public static bool Equals(object objA, object objB)
{
    return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
}

Note: As the ==-operator does not show polymorph behavior, it is equal to Object.ReferenceEquals in this case. You will see that the instance member Object.Equals is called by the static Object.Equals. So we can save explicit checks whether one of the parameters is null in the operators.

Comparison operators

When implementing IComparable<T> you should also overload the <, >, <= and the >= operators.

In addition you should provide a static method Compare which is used by the implementation of them.

Don’t forget that the parameters can be null here, either.

Overwriting GetHashCode

If you override Equals you should also override GetHashCode.

The MSDN says why:

The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object’s Equals method. Note that this is true only for the current execution of an application, and that a different hash code can be returned if the application is run again.

[…]

Derived classes that override GetHashCode must also override Equals to guarantee that two objects considered equal have the same hash code; otherwise, the Hashtable type might not work correctly.

Object.ReferenceEquals(other, null)

I marked that line with “Not mandatory” as the equality logic should always return true when passing the object itself as parameter. However, you will still find it in some Equals methods.

Dealing with class hierarchies

Another, very important point is implementing the equality members in class hierarchies. The problem is, that the following statements should always be fulfilled:

x.Equals(x) – x should equal itself

x.Equals(y) –> y.Equals(x) – if x equals y then y should equal x, too

x.Equaly(y) && y.Equals(z) –> x.Equals(z) – if x equals y and y equals z then x and z should be equal

The problem is the second line.

Lets have the following example:

class Program
{
    static void Main(string[] args)
    {
        Sub s = new Sub();
        s.Value = 1;
        s.Value2 = 2;

        Base b = new Base();
        b.Value = 1;

        bool c1 = s.Equals((object) b);
        bool c2 = b.Equals((object) s);
    }
}

public class Base
{
    public int Value { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Base);
    }

    public bool Equals(Base other)
    {
        if (Object.ReferenceEquals(other, null))
            return false;

        return this.Value == other.Value;
    }
}

public class Sub : Base
{
    public int Value2  { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Sub);
    }

    public bool Equals(Sub other)
    {
        if (Object.ReferenceEquals(other, null))
            return false;

        return base.Equals(other)
            && this.Value2 == other.Value2;
    }
}

Note: I left out some things for better concentrating on the important aspect and for shorting the code.

You will see that c1 does not equal c2 after executing the program. The reason is, that Base.Equals only compares the Value while Sub.Equals also includes Value2 respectively always returns false when passing a Base.

Solution 1: Only rely on the Equals implementation of the base class

Even if this solution has some serious disadvantages it can be a solution in some cases. Sub would contain no Equality logic then and Base would override Equals and GetHashCode as sealed.

Solution 2: Only allow comparisons of objects with the same type

This solution is much more common.

We have to modify our reference implementation as follows (I left out the comparison members for that example):

public class Base : IEquatable<Base>
{
    public static bool operator ==(Base first, Base second)
    {
        return Object.Equals(first, second);
    }
    public static bool operator !=(Base first, Base second)
    {
        return !Object.Equals(first, second);
    }

    public int Value { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Base);
    }
    public bool Equals(Base other)
    {
        if (Object.ReferenceEquals(other, null))
            return false;
        if (other.GetType() != typeof(Base))
            return false;

        return other.Value == this.Value;
    }
    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }
}

public sealed class Foo : Base, IEquatable<Foo>
{
    public int Value2 { get; set; }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Foo);
    }
    public bool Equals(Foo other)
    {
        if (Object.ReferenceEquals(other, null))
            return false;
        if (other.GetType() != typeof(Foo))
            return false;

        return base.Equals(other)
            && other.Value2 == this.Value2;
    }
    public override int GetHashCode()
    {
        return base.GetHashCode()
            ^ this.Value2.GetHashCode();
    }
}

Conclusion

Implementing the equality members is not always that simple.

With a good reference implementation, you can prevent writing redundant code and you don’t have to use Exception-based programming.

I hope my implementation does not contain any logical (or Copy&Paste ;-) ) errors. Please let me know if you find any.

//EDIT:

I changed the implementation of Equals(Foo other) so that it doesn’t call CompareTo as this can be dangerous in some situations.

About these ads

2 Responses to “Implementing (IComparable<T>), IEquatable<T> and the equality members”

  1. tobi Says:

    you could show how to specify the equals and compareto contract using code contracts.

  2. winsharp93 Says:

    >> you could show how to specify the equals and compareto
    That’s an idea.

    I will post this as an example in my “No interface without contract” series.

    Cheers
    winSharp93


Comments are closed.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: