Monthly Archives: August 2010

EqualityComparer<T>

I was recently reading a book which, whilst exposing a pretty cool thing, was reckless in its advice to readers. In a discussion of the IEqualityComparer<T> interface, the book asserts that “You’ll almost never need to write a full implmentation of IEqualityComparer<T> yourself. Instead, you can use the EqualityComparer<T> class and its Default property”.

This is not good advice. Take my last post, which uses a class that implements an IEqualityComparer<T>. Now, if I just blithely changed the code of the StudentComparer class to the following, my HashSet example would no longer work:

    class StudentComparerII : EqualityComparer<Student>
    {
        public override bool Equals(Student x, Student y)
        {
            return EqualityComparer<Student>.Default.Equals(x, y);
        }

        public override int GetHashCode(Student obj)
        {
            return EqualityComparer<Student>.Default.GetHashCode(obj);
        }
    }

Why? Because the thing which distinguishes one object from another is the GetHashCode() method. My IEqualityComparer<T>-implementing class implemented the GetHashCode() method of that interface. This class, inheriting from EqualityComparer<T>, does not. It uses the GetHashCode() method of the base Object class. And that is not going to work with this kind of comparison i.e. a value comparison.

I could, however, make this option work by making the Student class implement the IEquateable<T> interface. You can see in the following code how it does so.

    public class Student : IEquatable<Student>
    {
        public string Name { get; set; }
        public int ID { get; set; }
        public bool Vote { get; set; }

        public Student(string name, int id, bool vote)
        {
            Name = name;
            ID = id;
            Vote = vote;
        }

        public override int GetHashCode()
        {
            return ID.GetHashCode();
        }

        public bool Equals(Student other)
        {
            return this.ID == other.ID;
        }
    }

But more importantly, I overrode the GetHashCode() method in exactly the same way that I did in my last post with the IEqualityComparer<T> interface.

My point is, whilst this is a cool class to inherit from, with its Default property feature, you cannot just unknowingly use it hoping for the best. You need to understand how your comparer is going to work; how it will distinguish objects from one another. I was using value comparison rather than reference comparison. So the Default property thing was never going to add any benefits to me unless I burned the same amount of calories implementing the IEquatable<T> interface.