Click here to Skip to main content
15,867,985 members
Articles / All Topics

Delegate Your Equality Comparisons

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
14 Apr 2012CPOL 11K   1
Delegate your equality comparisons

When using Linq, you often need to distinct your query results. Therefore, you need to implement an IEqualityComparer for the more advance scenarios. For example, if you want to distinct on a specific property, or maybe on multiple properties. However this forces you to write lots of infrastructure code to distinct each type.

You probably would end up with several equality compare classes like this:

C#
public class ProductIdEqualityComparer : IEqualityComparer<Product>
{
    public bool Equals(Product x, Product y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(Product obj)
    {
        return obj.Id.GetHashCode();
    }
}

public class ProductPriceEqualityComparer : IEqualityComparer<Product>
{
    public bool Equals(Product x, Product y)
    {
        return x.Price == y.Price;
    }

    public int GetHashCode(Product obj)
    {
        return obj.Price.GetHashCode();
    }
}

public class PersonLastNameEqualityComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.LastName == y.LastName;
    }

    public int GetHashCode(Person obj)
    {
        return obj.LastName.GetHashCode();
    }
}

However there is a solution which will save you the work to write all this classes. You will have to write only two classes. One will contain some extension methods, the other has a DelegateEqualityComparer.

C#
public static class CompareExtensions
{
    public static IEnumerable<T> Distinct<T>
       (this IEnumerable<T> items, Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        return items.Distinct(new DelegateEqualityComparer<T>(equals, hashCode));
    }

    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateEqualityComparer<T>(equals, null));
    }
}

public class DelegateEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _equals;
    private readonly Func<T, int> _hashCode;
    public DelegateEqualityComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals = equals;
        _hashCode = hashCode;
    }

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

    public int GetHashCode(T obj)
    {
        if (_hashCode != null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }
}

Now you can simply distinct your query by providing a lambda. I tried it on IQueryable, but this doesn’t work. Linq will generate some SQL to do the actual query. We didn’t specify any code that can translate the equality comparer to SQL. If someone figures out how to make it work with IQueryable, please let me know.

C#
_products.Distinct((x, y) => x.Id == y.Id, x.Id.GetHashCode());
_products.Distinct((x, y) => x.Price == y.Price, x.Price.GetHashCode());
_persons.Distinct((x, y) => x.LastName == y.LastName, x.LastName.GetHashCode());
_persons.Distinct((x, y) => x.FirstName == y.FirstName, x.FirstName.GetHashCode());
_persons.Distinct((x, y) => x.Address.City == y.Address.City, x.Address.City.GetHashCode());

Share this article if you found it useful.

This article was originally posted at http://marcofranssen.nl?p=222

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Atos
Netherlands Netherlands
I am a .NET Software Developer at Atos International. Architecture, CQRS, DDD, C#, ASP.NET, MVC3, HTML5, Jquery, WP7, WPF, ncqrs, Node.js

Comments and Discussions

 
SuggestionFormatting Pin
Wendelius14-Apr-12 2:39
mentorWendelius14-Apr-12 2:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.