Skip to content

Sorting

by Alex Peck on September 25th, 2009

In the .NET 1.1 era, sorting is based on the IComparer and IComparable interfaces. I first realised this scheme is a little limited in a job interview. I tried to explain how I might sort a contact list on different properties. In my mind I envisaged something similar to the STL sort algorithm using a function pointer or functor. Unfortunately, at the time I was unable to express this in C#.

Let’s say our Contact data structure is simply this:

public class Contact : IComparable
{
   // constructor omitted, just assume it assigns fields
 
   public string Forename { get; set; }
   public string Surname { get; set; }
   public string PhoneNo { get; set; }
   public string EmailAddress { get; set; }
 
   public int CompareTo(object obj) 
   {
        Contact otherContact = obj as Contact; 
        if (otherContact != null)
            return this.Surname.CompareTo(otherContact.Surname);
        else
           throw new ArgumentException("Object is not a Contact");
   }
}

In order to sort a collection of Contacts, we can simply call the Sort method as follows:

ArrayList list = new ArrayList();
// add some Contacts
list.Sort();

That’s all well and good, but we will always sort by Contact.Surname. The collection Sort method will automatically use the IComparable CompareTo method defined by each Contact element. If we want to sort by a different property, say PhoneNo, we must use an IComparer.

public class ContactPhoneNoComparer : IComparer
{
   public int Compare(object left, object right)
   {
      Contact first = left as Contact;
      Contact second = right as Contact;
      // null checking omitted for brevity
      return first.PhoneNo.CompareTo(second.PhoneNo);
   }
}

To sort the Contact collection by PhoneNo we can now do this:

ArrayList contacts = new ArrayList();
// add some Contacts
contacts.Sort(new ContactPhoneNoComparer());

At least we can centralise our comparison logic in comparer classes, but this .NET 1.1 way of doing things is a bit ugly. Since we are forced to use object, the compiler can’t do any type checking to save us from our own stupidity. We end up having to write boiler plate code to check our casts. These comparer classes are also a little verbose, especially if we need a lot of them.

What follows is shameless plagarism: when I read chapter 1 of C# in Depth I realised that it contained what would have been a good answer to my interview question. This is a diversion from the framework foundation, but a worthy one.

IComparer<T>

The casting, which is really the most significant limitation, can be removed by using IComparer<T>. Note that this is now much shorter and more readable.

public class ContactPhoneNoComparer : IComparer<Contact>
{
   public int Compare(Contact left, Contact right)
   {
      return left.PhoneNo.CompareTo(right.PhoneNo);
   }
}

Using a lambda expression

Using a lambda expression we can eliminate the IComparer entirely.

List<Contact> contacts = new List<Contact>();
// add some Contacts
contacts.Sort((l, r) => l.PhoneNo.CompareTo(r.PhoneNo));

Using the OrderBy extension method

Finally, using an extension method, we can do something even more readable. This time, the original order of the list is preserved, and we get a new contact list in the specified order.

List<Contact> contacts = new List<Contact>();
// add some Contacts
List<Contact> sortedContacts = contacts.OrderBy(c => c.PhoneNo);
No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS