Post navigation

A Generic IEqualityComparer for Linq Distinct()

Defines methods to support the comparison of objects for equality. IEqualityComparer introduces a custom GetHashCode method.

I love Linq and I find myself using it more and more, but I am always mildly annoyed everytime I (re)discover that I can’t do a Distinct filter on a property of the class in my collection. For example, if I have a list of Contact objects and I want to extract from that list a distinct list of Contacts based on their email address. The parameter-lessDistinct() method will compare a Contact object based on the default equality comparer, but there is no quick way to specify that I want to compare them based on email address. This article describes a generic implementation of an IEqualityComparer that can be used by Distinct() to compare any class based on a property of that class.

The Problem

Nothing fancy there, just a class with some basic properties. And the problem we want to solve is that if we have a list of Contact objects where some contacts have the same email address, we want to get just a distinct list of email addresses by doing something like this:

But if we do this, Distinct will compare Contact objects based on the default equality comparer which will compare them by reference. In this case, Distinct will return all of the Contacts in our original collection (assuming they are all unique instances).

Solution 1: Override Default Equality Comparer

One solution to get Linq operate on the EmailAddress property would be to override the Equals andGetHashCode methods for the Contact class and have it use the EmailAddress property of the Contact. This would cause the parameter-less Distinct() method to use your override. Besides the fact that this method has subtle complications that make it tricky, you might not always want to compare Contact objects based onEmailAddress. You might also sometimes compare them based on Name. So the Equals operator may not be the best solution.

Solution 2: Implement IEqualityComparer<Contact>

The Distinct() method also has an overload which allows you to specify an IEqualityComparer implementation. So, another solution is to write a class that implements IEqualityComparer<Contact> and performs the comparison based on the EmailAddress property.

This will cause the Distinct() method to compare our objects based our custom Equals implementation which uses the EmailAddress property of the Contact.

A Generic Solution

The implementation of the ContactEmailComparer is pretty trivial, but it does seem like a lot of work just to get a distinct list of email addresses.

A more universal solution is to write a generic class where you can tell it which property of your objects to compare on. We will extend our IEqualityComparer to use reflection to extract the value of a specified property, rather than restricting our class to one property.