Tidy IEqualityComparer with GenericEqualityComparer

Whilst looking through a codebase, I saw implementations of IEqualityComparer<>. After thinking to myself that the need to create an entire implementation of IEqualityComparer<> per use creates quite a bit of boilerplate for such a small amount of signal, I realised that creating a generic implementation of IEqualityComparer<> that takes a definition of equality in its constructor would be very simple.

public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
	private readonly Func<T, T, bool> mEqualsFunc;
	private readonly Func<T, int> mGetHashCodeFunc;

	public GenericEqualityComparer(Func<T, T, bool> equalsFunc, 
		Func<T, int> getHashCodeFunc)
	{
		if (equalsFunc == null)
			throw new ArgumentNullException("equalsFunc");
		if (getHashCodeFunc == null)
			throw new ArgumentNullException("getHashCodeFunc");

		mEqualsFunc = equalsFunc;
		mGetHashCodeFunc = getHashCodeFunc;
	}

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

	public int GetHashCode(T obj)
	{
		return mGetHashCodeFunc(obj);
	}
}

Creating and using an instance of this class is as simple as

public class TestClass
{
	private static readonly GeneralEqualityComparer<Foo> mFooComparer = 
		new GeneralEqualityComparer<Foo>(
			(x, y) => x.Id == y.Id,
			obj => obj.Id);

	public void GetDistinctFoos(IEnumerable<Foo> foos)
	{
		return foos.Distinct(mFooComparer);
	}
}

However, I was a bit embarrassed when I told my boss, Tony Beveridge, about this great use of generics and Funcs I had thought of, and he told me he had actually implemented exactly the same class some months ago.

Its worth noting that EqualityComparer<T>.Default provides a default implementation using the Equals() and GetHashCode() functions of T.

If you wanted to extend GenericEqualityComparer so you don’t have to provide an implementation for GetHashCode(), you can default mGetHashCodeFunc to always return zero. This will force the Equals function to always be called.