How to override .Equals, .GetHashCode and implement IEquatable<T>

I will show a good way to code for Equality in types and I will point to some nasty pitfalls and things that may go wrong!

acorn_twins

This topic keeps coming up and today it did again in my company. It’s a classic and so many things while implementing this can go really pear shaped! Some things are as equal as two nuts. Other things are only as equal as two nuts. ;~)

Note: Almost two years ago I blogged about the advantages of using IEquatable<T> when overriding .Equals(): Be careful when you override Equals (and use IEquatable). I did not cover .GetHashCode() at that time.

First some of the guideline texts for reference

Please scroll down for the good stuff! ;~) There are code samples below!

In this post I will not discuss the Equality operator (==) very much but I will give you the guidelines for completeness:

<guildelines>

Guidelines for Implementing Equals and the Equality Operator (==) (Updated: November 2007)

The following rules outline the guidelines for implementing the Equals method and the equality operator (==):

  • Implement the GetHashCode method whenever you implement the Equals method. This keeps Equals and GetHashCode synchronized.

  • Override the Equals method whenever you implement the equality operator (==), and make them do the same thing. This allows infrastructure code such as Hashtable and ArrayList, which use the Equals method, to behave the same way as user code written using the equality operator.

  • Override the Equals method any time you implement the IComparable.

  • Consider implementing operator overloading for the equality (==), not equal (!=), less than (<), and greater than (>) operators when you implement IComparable.

  • Do not throw exceptions from the Equals or GetHashCode methods or the equality operator (==).

Implementing the Equals Method (Updated: November 2007)

For related information on implementing the equality operator (==), see Guidelines for Implementing Equals and the Equality Operator (==).

  • Override the GetHashCode method to allow a type to work correctly in a hash table.

  • Do not throw an exception in the implementation of an Equals method. Instead, return false for a null argument.

  • Follow the contract defined on the Object.Equals Method as follows:

    • x.Equals(x) returns true.

    • x.Equals(y) returns the same value as y.Equals(x).

    • (x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.

    • Successive invocations of x.Equals(y) return the same value as long as the objects referenced by x and y are not modified.

    • x.Equals(null) returns false.

  • For some kinds of objects, it is desirable to have Equals test for value equality instead of referential equality. Such implementations of Equals return true if the two objects have the same value, even if they are not the same instance. The definition of what constitutes an object's value is up to the implementer of the type, but it is typically some or all of the data stored in the instance variables of the object. For example, the value of a string is based on the characters of the string; the Equals method of the String class returns true for any two instances of a string that contain exactly the same characters in the same order.

  • When the Equals method of a base class provides value equality, an override of Equals in a derived class should call the inherited implementation of Equals.

  • If you are programming in a language that supports operator overloading, and you choose to overload the equality operator (==) for a specified type, that type should override the Equals method. Such implementations of the Equals method should return the same results as the equality operator. Following this guideline will help ensure that class library code using Equals (such as ArrayList and Hashtable) works in a manner that is consistent with the way the equality operator is used by application code.

  • If you are implementing a value type, you should consider overriding the Equals method to gain increased performance over the default implementation of the Equals method on ValueType. If you override Equals and the language supports operator overloading, you should overload the equality operator for your value type.

  • If you are implementing reference types, you should consider overriding the Equals method on a reference type if your type looks like a base type, such as Point, String, BigNumber, and so on. Most reference types should not overload the equality operator, even if they override Equals. However, if you are implementing a reference type that is intended to have value semantics, such as a complex number type, you should override the equality operator.

  • If you implement the IComparable interface on a given type, you should override Equals on that type.

Object.GetHashCode Method

A hash function must have the following properties:

  • If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.

  • 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.

  • For the best performance, a hash function must generate a random distribution for all input.

</guildelines>

Now if you follow these guidelines the equality operator (==) and the .Equals() should always be in sync. Personally I thing that is a good idea to avoid confusing for the poor souls that have to use your code! ;~) I have also heard the opposite but then .Euals might be value equals and == might be reference equals. First; when do you ever need both types of comparison in the same app? Second; this is bound to cause confusion and bugs!

And now the good stuff!

Some samples

Here are some interesting simple samples where you have to look out! My implementation is fine but the behavior might not always be as expected:

[TestFixture]
public class MiscTests
{
    #region test types
    class A : IEquatable<A>
    {
        public string Foo { get; set; }

        public bool Equals(A other)
        {
            return string.Equals(Foo, other.Foo);
        }

        public override bool Equals(object obj)
        {
            A a = obj as A;
            return a != null && Equals(a);
        }

        public override int GetHashCode()
        {
            return Foo != null ? Foo.GetHashCode() : base.GetHashCode();
        }
    }

    class AA : A, IEquatable<AA>
    {
        public string Foo2 { get; set; }

        public bool Equals(AA other)
        {
            return string.Equals(Foo, other.Foo) &&
                     string.Equals(Foo2, other.Foo2);
        }

        public override bool Equals(object obj)
        {
            AA aa = obj as AA;
            return aa != null && Equals(aa);
        }

        public override int GetHashCode()
        {
            return (Foo != null ? Foo.GetHashCode() : base.GetHashCode()) ^ 
                     (Foo2 != null ? Foo2.GetHashCode() : base.GetHashCode());
        }
    }

    class B : IEquatable<B>
    {
        public string Bar { get; set; }

        public bool Equals(B other)
        {
            return string.Equals(Bar, other.Bar);
        }

        public override bool Equals(object obj)
        {
            B b = obj as B;
            return b != null && Equals(b);
        }

        public override int GetHashCode()
        {
            return Bar != null ? Bar.GetHashCode() : base.GetHashCode();
        }
    }
    #endregion

    [Test]
    public void DiffrentTypesWithSameHashCode()
    {
        A a = new A { Foo = "foobar" };
        B b = new B { Bar = "foobar" };

        Assert.AreNotEqual(a, b);
        Assert.IsFalse(a.Equals(b));
        Assert.AreEqual(a.GetHashCode(), b.GetHashCode());
    }

    [Test]
    public void DiffrentInstancesWithSameHashCode()
    {
        AA aa1 = new AA { Foo = "foo", Foo2 = "foo" };
        AA aa2 = new AA { Foo = "bar", Foo2 = "bar" };

        Assert.AreNotEqual(aa1, aa2);
        Assert.IsFalse(aa1.Equals(aa2));
        Assert.AreEqual(aa1.GetHashCode(), aa2.GetHashCode());
    }

    [Test]
    public void DiffrentEqualsBehaviors()
    {
        A a = new A { Foo = "foo" };
        AA aa = new AA { Foo = "foo", Foo2 = "bar" };

        Assert.IsTrue(a.Equals(aa));
        Assert.IsTrue(a.Equals((object)aa)); // Now we rund the old fashioned AA.Equals(object) on A and that makes the two equal;

        Assert.IsTrue(aa.Equals(a)); // ??? => The .Equals that will be executed is the one that takes A as a parameter making the instances equal!
                                              // Ignoring the fact that the comparer is a subtype of the instance compared.
        Assert.IsFalse(aa.Equals((object)a)); // Now we run the old fashioned A.Equals(object) on AA and that makes the two not equal;
    }
}

The good Equality implementation

Finally I’d like to show what I consider to be a good implementation of equality on a ‘Person’ type. The trio (.Equals(), .GetHashCode() and IEquatable<Person>) are all implemented and the type has both value types, reference types and complex reference types.

public classPerson: IEquatable<Person>
{
    public stringName { get; set; }
    public intShoesize { get; set; }
    publicPersonFather { get; set; }

    public override boolEquals(objectobj)
    {
        Personp = obj asPerson;
        returnp != null&& Equals(p);
    }

    public boolEquals(Personother)
    {
        returnother != null&&
                 Name == other.Name &&
                 Shoesize == other.Shoesize &&
                 Father == other.Father;
    }

    public override intGetHashCode()
    {
        return((!string.IsNullOrEmpty(Name)) ? Name.GetHashCode() : base.GetHashCode()) ^
                 Shoesize.GetHashCode() ^
                 (Father != null? Father.GetHashCode() : base.GetHashCode());
    }
}

Cheers,

/Magnus

Technorati Tags:

posted @ Friday, October 10, 2008 12:37 AM

Print

Comments on this entry:

# re: How to override .Equals, .GetHashCode and implement IEquatable<T>

Left by Lewis Moten at 10/23/2010 5:50 AM
Gravatar
One little problem I see with your proposed solution. You need to check that the type matches too. For example, Employee may inherit from Person. Your implementation would say that Person.Equals(Employee), but may or may not say Employee.Equals(Person) due to polymorphism. You can work around this by checking the type as well.

public bool Equals(Person other)
{
return other != null &&
other.GetType() == typeof(Person) &&
Name == other.Name &&
Shoesize == other.Shoesize &&
Father == other.Father;
}

Your comment:



 (will not be displayed)


 
 
 
Please add 4 and 5 and type the answer here:
 

Live Comment Preview: