Sometimes, you want to be able to sort objects on a number of different fields. For example, employees can be sorted by their name, years of service, pay rate; or blog posts by the date they were posted, their status, number of comments, the author. It's really easy to offer all these sorting methods. Best of all you don't have to write too much code.

Screenshot of a Console Application That Uses Custom Sorting

btw, in totally unrelated news --- I finally added appropriate titles to the entry pages, so now you should see the entry title in the title bar instead of "SimplyGold | Notebook." ;-)

Default Sorting

Suppose we have a Politician class:

public class Politician
{
 private string ssn;
 private string name;
 private int powerIndex;
 private int voterBaseIndex;
 private int suckitudeIndex;

 public string SSN
 {
  get { return this.ssn; }
 }

 public string Name
 {
  get { return this.name; }
 }

 public int PowerIndex
 {
  get { return this.powerIndex; }
 }

 public int VoterBaseIndex
 {
  get { return this.voterBaseIndex; }
 }

 public int SuckitudeIndex
 {
  get { return this.suckitudeIndex; }
 }
}

We want to allow the users to sort by name, powerIndex, voterBaseIndex, or the suckitudeIndex. We feel that people generally want to sort the politicians by their suckitudeIndex. So we're going to have the default sorting method sort by the suckitudeIndex while giving users the option of sorting by various other indices, too.

Default Sorting: IComparable

To tell .NET how to sort objects by default, all we need to do is inherit from the IComparable interface:

public class Politician : System.IComparable<Politician>

You notice that we're using the generic interface, which takes in an object of the type we're interested in, instead of the normal IComparable interface, which takes in whatever object.

The IComparable interface has a single method that we need to implement: CompareTo; the implementation is straight-forward:

public int CompareTo(Politician politician)
{
 // NULL is considered to be smaller than
 // any other value.

 if (politician == null)
 {
  return 1;
 }

 else if (this.suckitudeIndex < politician.suckitudeIndex)
 {
  return -1;
 }

 else if (this.suckitudeIndex == politician.suckitudeIndex)
 {
  return 0;
 }

 else
 {
  return 1;
 }
}

Sorting by Power Index: IComparer

A class can sort by any number of fields or any combination of fields. We do this by creating classes that implement the IComparer.

For example:

public class PowerIndexComparer : System.Collections.Generic.IComparer<Politician>
{
 public int Compare(Politician first, Politician second)
 {
  if (first == null & second == null)
  {
   return 0;
  }

  else if (first == null)
  {
   return -1;
  }

  else if (second == null)
  {
   return 1;
  }

  else if (first.powerIndex < second.powerIndex)
  {
   return -1;
  }

  else if (first.powerIndex == second.powerIndex)
  {
   return 0;
  }

  else
  {
   return 1;
  }
 }
}

And when you want to sort politicians by the power index, simply pass along the IComparable object to the sort method:

politicians.Sort(new PowerIndexComparer());

Usually, I create the IComparable classes as private classes within the main class. For example, the PowerIndexComparer would be declared as a private class within the Politician class. This was how I saw it done in one of the books I read. It makes sense, because all comparer classes aren't independent/full-fledged classes but rather part of the Politician class. So the way this would work:

public class Politician : System.IComparable<Politician>
{
 private class PowerIndexComparer : System.Collections.Generic.IComparer<Politician>
 {
  public int Compare(Politician first, Politician second)
  {
   if (first == null & second == null)
   {
    return 0;
   }

   else if (first == null)
   {
    return -1;
   }

   else if (second == null)
   {
    return 1;
   }

   else if (first.powerIndex < second.powerIndex)
   {
    return -1;
   }

   else if (first.powerIndex == second.powerIndex)
   {
    return 0;
   }

  else
  {
    return 1;
   }
  }
 }

 public static System.Collections.Generic.IComparer<Politician> PowerIndexSorter
 {
  get { return new PowerIndexComparer(); }
 }
}

So when you want to sort by power index:

politicians.Sort(Politician.PowerIndexSorter);

I found the book I mentioned --- Visual C# 2005 Recipes: A Problem-Solution Approach.

Conclusion & Downloads

Obviously, if it's unlikely that a user is going to want to sort by a field, don't give him the choice just for the heck of it. For example, it makes no sense to give the user the ability to sort a blog post by its body. (And even if some user wants to do it for some odd reason, she can do it herself.) You can use the normal (i.e., non-generic) IComparer and ICopmarable interfaces, in much the same way, if you're not into generics. You simply have to add another check to make sure that you're comparing objects of the same type.

Here's is a link to the full Politician class, which implements all sort methods mentioned in the beginning of the class as well as (commented-out) sample usage.