Wednesday, November 22, 2006

In .NET some of the collection types such as Array, ArrayList, List<T> expose the Sort method that allows you to sort the collection. In order for those classes to sort the collection, there has to be a way for them to somehow compare objects with each other. The most basic Sort method usually does not require any arguments other than the collection itself in case of the static Array.Sort. In this case how does the framework know how to compare our objects? Is person1 greater than person2? Or maybe they are equal?

Enter the IComparable interface. By implementing this interface, the objects of the type become, as the name suggest, comparable, so now it is possible to compare person1 to person2 using a simple person1.CompareTo(person2) call. With this each Sort method can easily handle sorting. An example implementation of sort method is included in the MSDN documentation.

So what is wrong there? If you take a Int32 objects, a String object, the IComparable interface on those is pretty straightforward and self explaining. It is plain even to a non-technical person that 1 < 3 and "abc" = "abc". But what about a more complex types such as a custom Person type that represents a person in the real world.

What does it mean for one persons to be greater (or equal for that matter) than the other? It does not mean anything without a context, a criteria by which we compare the two. So for example if were to compare the age of both persons, we would be able to say if who is older (greater) or younger (lesser). We could compare their names, their salary or whatever, but we need to know the criteria before we start.

Suppose that we have a class definition of Person as follows:

public class Person : IComparable
{
  int age;
  string name;

What does it mean for an object of this class to be comparable to each other? Is the name used? Or maybe the age? The fact that we don't know! The only way to know is to check the documentation or the implementation of the CompareTo method. First one goes against the rule I like to follow that states that the code should be self documenting. In this case it isn't. The second option breaks the rule of encapsulation - that is, we have to know the implementation of an object to work with it. Fortunately there is another way to compare and sort objects.

Enter the IComparer interface. This interface allows you to compare two objects in a similar way the IComparable interface does. The difference here is that it IComparer allows for much greater expressiveness. An implementation of IComparer is always a class which does have a name. This name explains (or at least it should) exactly what it does. For example:

public class PersonAgeComparer : IComparer

Makes it obvious to everyone what will be the criteria for the comparison without breaking the encapsulation. This makes your code easier to understand and much more self documenting. I strongly discourage everyone from using the IComparable interface on classes where it is not explicit what would be compared, even if it will be the only comparison made.

kick it on DotNetKicks.com