Chris's coding blog

Equals vs IEqualityComparer, IEquatable<T>, IComparable, IComparer

September 27, 2009

This is a look at the difference between Equals vs IEqualityComparer, IEquatable, IComparable, IComparer which are used for sorting and comparisons.

Cheatsheet

The TLDR version:

  • Distinct() - override Equals() and GetHashcode() in your object. If T is an interface, then pass a IEqualityComparer<T> implementation.
  • Dictionary<T> - your T should override Equals() and GetHashcode(). If T is an interface, then pass a IEqualityComparer<T> implementation.
  • Contains and IndexOf also follow the rules above.

Equals

Besides object comparison, Equals is also used in a few common collection methods. IndexOf(), Contains() both use the Equals() method for equality comparisons. This extends even further with .NET 3.5 and the inclusion of LINQ into the equation, where Equals is used frequently for comparion of methods like Distinct.

IEquatable

This interface was added in .NET 2.0. Its primary use is with value types to avoid the use of ValueType.Equals() (which all structs automatically use if Equals isn’t overriden). ValueType.Equals uses reflection to iterate through every field in the struct, checking for equality. If they are all equal to the object passed then true is returned (see the source further on in the article). Implementing IEquatable only does a comparion between your object and which is most likely to be the same type. This is performed frequently with the new methods brought in alongside LINQ in 3.5. Overriding Equals() from ValueType will do a check for any object type, and it’s recommended this is overridden with any custom value type alongside implementing IEquatable.

IEqualityComparer

This interface, also added in 2.0, allows you to pass custom object equality checking to a Hashtable, Dictionary and NameValueCollection. You pass in your custom comparison implementation much like you do with IComparer and the Hashtable will use this for equality checks.

The main use of this interface is you can write a single implementation of IEqualityComparer<T> which several classes can then use, removing the need to rewrite the comparison logic for every class. For example if you class implements IEntity which has an Id property, you could then use a comparison class for all equality checks that implements IEqualityComparer<IEntity>.

IComparer

This is used for custom sorting of objects. Prior to .NET 3.5 it was used primarily for Array.Sort but now finds itself used for sorting with LINQ. It has several default implementations including (as the MSDN page says) Comparer, CaseInsensitiveComparer. When implementing the interface, there is one method to fill:

int Compare(Object x,Object y)
Returns:
x < y       -1
x == y      0
x > y       1

IComparer allows you to specialise the comparison by type.

Using IComparer with C# 3.0 closures

With the introduction of closures in C# 3.0, you can now shorten your code with IComparer and sorting. Here’s an example with sorting items:

List<ContentItem> list = N2.Context.CurrentPage.Children.ToList<ContentItem>();
list.Sort(delegate(ContentItem x, ContentItem y)
{
if (string.IsNullOrEmpty(x.Title))
return 1;
else if (string.IsNullOrEmpty(y.Title))
return -1;
return x.Title.CompareTo(y.Title);
});
view raw gistfile1.cs hosted with ❤ by GitHub

IComparable

IComparable gives you one method to implement:

int CompareTo(Object obj)

The same return value system applies as IComparer. The difference between this and IComparer is IComparable will be implemented on the class that contains your field values, while IComparable derived classes are used for custom sorting. So for example:

public class User : IComparable
{
public int CompareTo(Object obj)
{
// comparison code here
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

The CompareTo method would be the default way of ordering your User class, perhaps comparing by Name. If you then decided that you wanted to sort a list of User objects by another property, say Age, you could write a class that implemented IComparer instead of altering CompareTo inside User. This would perform the custom sorting, for example:

public class UserAgeComparer : IComparer
{
public int CompareTo(Object x, Object y)
{
User userX = x as User;
User userY = y as User;
if (userX != null & userY != null)
{
// compare the age.
}
else
{
return 0;
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

As with IComparer you can specialise the comparison by implementing IComparable.

csharp

I'm Chris Small, a software engineer working in London. This is my tech blog. Find out more about me via GithubStackoverflowResume