A Generic Equality Comparer for LINQ

LINQ operators generally use lambda expressions to control their processing and output. Some operators use IEqualityComparer<T> implementations to compare values. This article describes a generic comparer, driven by delegates, designed for use in queries.

IEqualityComparer<T>

In an earlier article I described how you can implement the IEqualityComparer<T> interface in order to create a class that compares values and determines if they are equal. Instances of such a class can be passed to standard .NET framework methods to allow complete control over the comparisons that they perform. For example, if you have a collection of Customer objects, you may wish to find all of the unique customers. Using Language Integrated Query's (LINQ) Distinct method without a comparer would generate a sequence of unique object references. With a custom comparer, you could specify that only the Idproperty defines uniqueness and eliminate all customers with duplicate IDs.

If you are using LINQ's standard query operators most of the actions that you execute will likely be defined using lambda expressions. The results of your query may often be returned as a sequence of anonymous type instances. It may be that the only named types that you use are for objects such as IEqualityComparer<T> instances that modify the internal behaviour of the LINQ extension methods. Whilst you are working with lambda expressions, you may decide that it would be better to use a generic comparer that can also be controlled with such delegates, rather than implementing multiple comparers, each with a slightly different effect. This article describes such a generic comparer, named LambdaComparer<T>.

Creating the LambdaEqualityComparer<T> Class

Let's begin by creating the class declaration for the comparer. In the downloadable code I've created this within a console application project. In a real-world scenario you might include the class in a dynamic linked library.

The declaration is shown below. The class is defined as public with a single generic type parameter. It implements IEqualityComparer<T> so that it can be used with LINQ operators and other classes within the .NET framework that accept such a type.

public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
}

Adding the Constructor

The comparer's functionality is set when it is instantiated by passing two lambda expressions to its constructor. The first accepts two objects of the type defined in the type parameter, "T". It defines how the two objects are compared and returns a Boolean value that is true if they match. The second delegate accepts one object and returns its hash code. Both lambda expressions are stored in private fields for later use. The code below should be added to the class to define the fields and the constructor. Note that the two Func delegates are validated to ensure neither is null.

NB: When you use the comparer you must ensure that the lambda expressions follow the rules defined for IEqualityComparer<T>. For example, the hash codes for two matching items must be the same and the order of comparison of two objects must not matter.

If you have read the previous article you will have seen the test code, where three customers were created and compared, and where their hash codes were calculated to show that matching customers had the same hash code. We can recreate that code using the LambdaEqualityComparer<T> class, as shown below. Note the two arguments passed to the comparer's constructor, which define the actions of the Equals and GetHashCode methods.

We can also try passing the comparer to a standard query operator. The code below creates a list containing the three customers. It then obtains a distinct sequence from the list. As the first and second customers have the same ID, customer2 is not present in the resultant sequence.