Using IComparable<T> Interface

Level : Beginner to Intermediate

C# language has constantly evolved over a constant period of time.Each new version introduced new features which changed the way we programmed and solved the problems. Whether it was introduction of generics in C# 2.0 , LINQ in C# 3.0 or concept of dynamic programming in C# 4.0 , each of them had or will have greater impact on our programming style.As a developer we don’t have much option but to evolve and redefine our self in this constantly changing environment. That said , I personally think that its even more important now to understand the fundamentals and central concept of C#. Its only through the proper understanding of these fundamental concepts , that we can get full grasp on advanced concepts like lambda expressions or LINQ. So with the overall intent being made clear , lets start with IComparable<T> interface.

IComparable<T>

Lets start with a very basic example wherein I will be creating a List of integer and then I will call the Sort() method on that list.

List<int> lstInt = new List<int>() { 23, 2, 1, 34, 12, 17 };
lstInt.Sort();
 
foreach (Int32 number in lstInt)
{
     Console.WriteLine(number.ToString());
}

OutPut :- 1,2,12,17,23,34

In here , Sort() method sort’s the list of integers. If we can recall from what we studied in data structures , sorting is implemented by algorithms like Bubble sort , merge sort etc.Internally .Net framework uses which sorting technique is not a matter of concern for us.All I know and care about is that , for sorting I should be able to compare two values. i.e. I should be able to compare 23 with 2. And in .Net framework types make themselves comparable by implementing IComparable<T> interface.So in the above case ,  integer type internally implements IComparable<T> interface.In short while we will see how Integer type implements IComparable<T> interface,but before that lets see the signature of IComparable<T> interface.

IComparable<T> Interface

   1:  public interface IComparable<T>
   2:  {
   3:      // Methods
   4:      int CompareTo(T other);
   5:  } 

Fairly simple , just one method which returns an integer.The possible values that CompareTo can return are –1 , 0 , 1. –1 is when initial value is less than the later one , 0 when both are equal and 1 when initial value is greater than later one.

   1:  if(a < b) 
   2:      return -1
   3:  else if(a > b)
   4:      return 1
   5:  else
   6:      return 0;

Next very quickly lets have a look at how System.Int32 type implements IComparable<T> interface.

   1:  public int CompareTo(int value)
   2:  {
   3:      if (this < value)
   4:      {
   5:          return -1;
   6:      }
   7:      if (this > value)
   8:      {
   9:          return 1;
  10:      }
  11:      return 0;
  12:  }

In here “this” corresponds to the current value and “value” parameter is the number with which it is being compared.Just like System.Int32 , all other primitive types(types defined by .Net framework e.g. DateTime , String , Char etc) implements IComparable<T> interface.

I have already spoken quiet a lot on IComparable<T> interface but till now I have constantly avoided a very important piece of information.In .Net framework there exists another non generic interface called IComparable.Well if you are programming in .Net framework 2.0 or above , then you need not worry about implementing this interface.All .Net primitive type implements both the generic as well as non generic version of IComparable interface.Interested people can search for more information on IComparable interface.

All right with the basics on IComparable<T> interface clear, lets understand its applicability in real time scenario.Quiet naturally , things tend to get complex as we start writing complex programs.In day to day programming , its very common to create list containing instances of our own custom types e.g Person , Employee etc. Consider the following code having list containing instances of type “Person”.

   1:  public class SampleCode
   2:      {
   3:          static void Main(string[] args)
   4:          {
   5:              List<Person> lstPerson = new List<Person>()
   6:              {
   7:                  new Person{Name="Mark",Age=23,Country="USA"},
   8:                  new Person{Name="Tom",Age=35,Country="UK"},
   9:                  new Person{Name="Ram",Age=25,Country="India"},
  10:                  new Person{Name="Clark",Age=28,Country="Australia"},
  11:                  new Person{Name="Jeff",Age=31,Country="France"},
  12:                  new Person{Name="Matt",Age=38,Country="Germany"},
  13:              };
  14:   
  15:              Console.ReadLine();
  16:          }
  17:      }
  18:   
  19:      public class Person
  20:      {
  21:          public int Age { get; set; }
  22:          public string Name { get; set; }
  23:          public string Country { get; set; }
  24:   
  25:          public override string ToString()
  26:          {
  27:              return Name + " : " + Country + " : " + Age.ToString();
  28:          }
  29:      }

If I try to call Sort() method on lstPerson i.e. lstPerson.Sort() , then I will get an unhandled exception with message “Failed to compare two elements in the array.” Quiet naturally , C# compiler is not smart enough to figure out which instance of type “Person” should come first etc.We haven't given any information to compiler on this issue.So lets make our Person class sort able by implementing IComparable<T> interface(learn things the hard way :) ) …..

There is absolutely nothing magical in making “Person” class sort able. Implmenet IComparable<T> interface and in that implementation , specify the basis on which records have to be sorted.In above case I want the records to be sorted as per their age in ascending order.Following is the new updated code snippet :-

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:   
   6:  namespace ForumApplications
   7:  {
   8:      public class SampleCode
   9:      {
  10:          static void Main(string[] args)
  11:          {
  12:              List<Person> lstPerson = new List<Person>()
  13:              {
  14:                  new Person{Name="Mark",Age=23,Country="USA"},
  15:                  new Person{Name="Tom",Age=35,Country="UK"},
  16:                  new Person{Name="Ram",Age=25,Country="India"},
  17:                  new Person{Name="Clark",Age=28,Country="Australia"},
  18:                  new Person{Name="Jeff",Age=31,Country="France"},
  19:                  new Person{Name="Matt",Age=38,Country="Germany"},
  20:              };
  21:              
  22:              // Sort the List
  23:              lstPerson.Sort();
  24:         
  25:              // Display the result
  26:              foreach (Person person in lstPerson)
  27:              {
  28:                  Console.WriteLine(person.ToString());
  29:              }
  30:   
  31:              Console.ReadLine();
  32:          }
  33:      }
  34:   
  35:      public class Person : IComparable<Person>
  36:      {
  37:          public int Age { get; set; }
  38:          public string Name { get; set; }
  39:          public string Country { get; set; }
  40:   
  41:          public override string ToString()
  42:          {
  43:              return Name + " : " + Country + " : " + Age.ToString();
  44:          }
  45:   
  46:         // Implement IComparable interface
  47:          public int CompareTo(Person other)
  48:          {
  49:              if (this.Age > other.Age)
  50:                  return 1;
  51:              else if (this.Age < other.Age)
  52:                  return -1;
  53:              else
  54:                  return 0;
  55:          }
  56:      }
  57:  }

Introducing IComparer Interface

Programming like life is full of surprises.Just when you thought you have understood IComparable interface , I have to introduce another big player in sorting functionality i.e. IComparer.

By default when every we call Sort() on our list which contains instance of our custom type , it will make use of the default implementation of CompareTo method of IComparable interface.In above case , we are sorting on the basis of “Age”.But what if tomorrow , there is a need to sort on the basis of “Country”. For these scenarios , where custom sorting routine is required we make use of IComparer interface.Following is the way we use IComparer interface.

On the existing class implement IComparer(generic version) interface . Specify the type parameter as the type of class itself(in our case its “Person”) and the provide the implementation of “Compare” method.Following code snippet explains the same more clearly :-

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:   
   6:  namespace ForumApplications
   7:  {
   8:      public class SampleCode
   9:      {
  10:          static void Main(string[] args)
  11:          {
  12:              // Code removed for brevity purpose
  13:              ..........
  14:   
  15:              // Create an instance of IComparer type ( programming to supertype concept)
  16:              IComparer<Person> compPerson = new Person();
  17:   
  18:              // use the overload of Sort method which takes IComparer instance
  19:              lstPerson.Sort(compPerson);
  20:   
  21:              foreach (Person person in lstPerson)
  22:              {
  23:                  Console.WriteLine(person.ToString());
  24:              }
  25:   
  26:              Console.ReadLine();
  27:          }
  28:      }
  29:   
  30:      public class Person : IComparable<Person> , IComparer<Person>
  31:      {
  32:          // Code removed for brevity purpose 
  33:          ...
  34:   
  35:          // Implement IComparer.Compare method
  36:          public int Compare(Person x, Person y)
  37:          {
  38:              return x.Country.CompareTo(y.Country);
  39:          }
  40:      }
  41:  }

Another important point here is that you can also create a new class and implement IComparer interface on it.Then in place of our  existing class instance you will have to make use the instance of this new class.This is all that can be said on IComparer interface implementation.

Template Method Pattern

What's This ? If we look closely , then internally .Net Framework handles the sorting algorithm.It can be making use of quick sort or merge sort etc but it has left certain component of that algorithm to be implemented or decided by the users of that implementation.In other way , high level components ( Collection class provided by .net framework in our case) controls the algorithm ( sorting algorithm ) but allows the the low level components (which uses high level component i.e. “Person” class in our case)  to provide implementation of certain portion of that algorithm. In above example we are providing the logic for comparison.This pattern is well known as “Template Method Pattern”.

Closing Notes

I hope just like I got benefitted by writing this article , other too will get their share once they have gone through this article.Going forward I will be concentrating on other interfaces like IEnumerable etc.Till then happy programming  ……..

Contact Me

rk.pawan@gmail.com | +1-737-202-7676 | LinkedIn | Facebook | Github |

8 Comments

Comments have been disabled for this content.