BlackWaspTM
LINQ
.NET 3.5+

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 Id property 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 LambdaComparer<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.

Func<T, T, bool> _equalsFunction;
Func<T, int> _hashCodeFunction;

public LambdaEqualityComparer(
    Func<T, T, bool> equalsFunction, Func<T, int> hashCodeFunction)
{
    if (equalsFunction == null) throw new ArgumentNullException();
    if (hashCodeFunction == null) throw new ArgumentNullException();

    _equalsFunction = equalsFunction;
    _hashCodeFunction = hashCodeFunction;
}

Implementing the Interface

We now need to implement the two methods described in the IEqualityComparer<T> interface. These call the appropriate stored Func delegate and return the results.

public bool Equals(T x, T y)
{
    return _equalsFunction(x, y);
}

public int GetHashCode(T obj)
{
    return _hashCodeFunction(obj);
}
16 January 2012