implementing a generic IEqualityComparer and IComparer class

Today i will write about the generic implementation of IComparer and IEqualityComparer. I read about this topic some times ago and now i wrote a linq join where i needed a specific EqualityComparer. So i decided to implement a generic version. The base of the Implementation is the generic IEqualityComparer<T> interface,

public interface IEqualityComparer<in T>
{
  bool Equals(T x, T y);
  bool int GetHashCode(T x);
}

and the generic IComparer<T> interface.

public interface IComparer<in T>
{
  int Compare(T x,T y)
}

If you use the IEqualityComparer<in T> interface you have to implement the Equals method that checks the equality of the two overtaken objects of type T and you have to implement the GetHashCode method that generates a customized  hashcode for the comparer object. This IEqualityComparer interface is widley used in Linq. For example at the Join, GroupJoin or Except extension methodes.

If you use the IComparer<in T> inferface you have to implement the Compare method that takes two objects of type T and compares them. If a is smaller than b it returns -1 if a and b are equal it returns 0 and if a is bigger than b it returns 1. The IComparer interface is used to sort datastructures. The List<T>.Sort method takes a comparer to decide how to sort a list.

The generic implemetation of the two interfaces is very easy. We use Func delegates to overtake the methodes dynmically and call that delegates if the Equals, GetHashCode and Compare methodes are called. Here is the implementation of the EqualityComparer.

public class EqualityComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> equalsFunction;
    private Func<T, int> getHashCodeFunction;

    public EqualityComparer(Func<T, T, bool> equalsFunction)
    {
        this.equalsFunction = equalsFunction;
    }

    public EqualityComparer(Func<T, T, bool> equalsFunction, 
        Func<T, int> getHashCodeFunction) : this(equalsFunction)
    {
        this.getHashCodeFunction = getHashCodeFunction;
    }

    public bool Equals(T a, T b)
    {
        return equalsFunction(a, b);
    }

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

And the use of the Equality Comparer:

List<Person> firstPersonList = new List<Person>() 
    { new Person() { Name = "mathias", Age = 36 }, 
        new Person() { Name = "karoline", Age = 35 } };

List<Person> secondPersonList = new List<Person>() { 
    new Person() { Name = "karl", Age = 36 }, 
    new Person() { Name = "sepp", Age = 36 }, 
    new Person() { Name = "emil", Age = 35 }, 
    new Person() { Name = "mia", Age = 35 }, 
    new Person() { Name = "hans", Age = 36 } };

EqualityComparer<Person> equalityComparer = new
    EqualityComparer<Person>((s1, s2) => s1.Age == s2.Age);

var groupJoinItems = firstPersonList.GroupJoin(
    secondPersonList, 
    p => p, 
    p => p,
    (p1, p2) => new { Name = p1.Name, Person = p2 }, 
    equalityComparer);

foreach (var item in groupJoinItems)
{
    Console.WriteLine("Name {0}", item.Name);
    foreach (var groupItem in item.Person)
    {
        var secondname = groupItem.Name;
        Console.WriteLine(" Name {0} / Age {1}", 
            groupItem.Name, groupItem.Age);
    }
}

Result:

Here is the implementation of the Comparer.

public class Comparer<T> : IComparer<T>
{
    private Func<T, T, int> comparerFunction;

    public Comparer(Func<T, T, int> comparerFunction)
    {
        this.comparerFunction = comparerFunction;
    }

    public int Compare(T a, T b)
    {
        if (object.ReferenceEquals(a, b))
            return 0;
        return comparerFunction(a, b);
    }
}

And the use of the Comparer<T>

List<Person> personList = new List<Person>() { 
    new Person() { Name = "karl", Age = 36 }, 
    new Person() { Name = "sepp", Age = 23 }, 
    new Person() { Name = "emil", Age = 3 }, 
    new Person() { Name = "mia", Age = 2 }, 
    new Person() { Name = "hans", Age = 26 } };

Comparer<Person> comparer =
    new Comparer<Person>(
        (p1, p2) => (p1.Age < p2.Age) ? -1 : (p1.Age == p2.Age) ? 0 : 1);        

personList.Sort(comparer);

foreach (var item in personList)
{
    Console.WriteLine("Name {0} / Age {1}", item.Name, item.Age);                
}

Result:

Using this generic version of IEqualityComparer<T> and IComparer<T> has two benefits. The first benefit is that you dont need to implement objects that implement the interfaces over and over again. The second benefit is that you can use lambda expression syntax to generate the body of the Equals and Compare methodes.