Designing Sortable Collections using IComparable

.NET Framework Class Library provides several interfaces to support additional functionality in collections such as ICollection, IList, and IComparable.

Implementing IComparable

IComparable interface is used to support the sorting of collections. It has one method - CompareTo. When you are designing sortable objects, you must implement the CompareTo method of IComparable.

For example, I create a Developer class, which is inherited from IComparable.

using System;
public class Developer: IComparable
{
    public string Name { get; set; }
    public int ExperienceInYears { get; set; }
    // Constructor
    public Developer(string name, int experienceInYears)
    {
        Name = name;
        ExperienceInYears = experienceInYears;
    }

    // Implement the CompareTo method from IComparable
    public int CompareTo(object obj)
    {
        if (obj == null) return 1; // Null is considered greater
        if (obj is Developer otherDeveloper)
        {
            // Compare based on experience (you can define your own comparison logic here)
            return ExperienceInYears.CompareTo(otherDeveloper.ExperienceInYears);
        }
        throw new ArgumentException("Object is not a Developer");
    }
}

Now I implement the CompareTo method like the following.

public int CompareTo(object obj)
{
    throw new NotImplementedException();
}

Now let's say the Developer class has a property called ID, and you want your items to be sorted on ID; we simply add the following code.

public int CompareTo(object obj)
{
    if (!(obj is Developer))
    {
        throw new InvalidCastException("Not a valid Developer object.");
    }
    Developer developer = (Developer)obj;
    return this.ID.CompareTo(developer.ID);
}

As you can see from the above code, it basically compares the current value of the object with the new value.

Now let's say the Developer object has public properties called - FirstName, LastName, Age, and Experience, and you want the sorting can be done based on any of these properties.

To do so, I defined an enumeration.

public enum SortFilter
{
    FirstName,
    LastName,
    Age,
    Experience
}

After that, I add a static property to the Developer class of type SortFilter.

private static SortFilter sortingBy = SortFilter.FirstName;
public static SortFilter SortingBy
{
    get { return sortingBy; }
    set { sortingBy = value; }
}

Now I change the CompareTo method to the following.

public int CompareTo(object obj)
{
    if (!(obj is Developer))
    {
        throw new InvalidCastException("Not a valid Developer object.");
    }
    Developer developer = (Developer)obj;
    switch (Developer.SortingBy)
    {
        case SortFilter.FirstName:
            return this.FirstName.CompareTo(developer.FirstName);
        case SortFilter.LastName:
            return this.LastName.CompareTo(developer.LastName);
        case SortFilter.Age:
            return this.Age.CompareTo(developer.Age);
        case SortFilter.Experience:
            return this.Experience.CompareTo(developer.Experience);
        default:
            goto case SortFilter.FirstName;
    }
}

As you can see from the above code, I have a case statement for each filter. Now my class is ready to support sorting. All I have to do is, create an ArrayList of Developer objects, set the sorting filter, and call the Sort method.

The following code creates an ArrayList of Developer objects.

private ArrayList GetList()
{
    ArrayList list = new ArrayList();
    list.Add(new Developer("Mahesh", "Chand", 30, "C#,ASP.NET,Windows Forms", 10));
    list.Add(new Developer("Michael", "Gold", 35, "GDI+, ASP.NET", 15));
    list.Add(new Developer("Bhasker", "Das", 26, "VB.NET, Web Applications", 4));
    list.Add(new Developer("Ashish", "Singhal", 24, "ADO.NET, GDI+", 4));
    list.Add(new Developer("Neel", "Beniwal", 3, "C#,ASP.NET,Windows Forms", 0));
    list.Add(new Developer("Melanie", "Talmadge", 25, "Java", 2));
    return list;
}

The following code sets the SortingBy filter as "FirstName" and calls the Sort method of ArrayList. Now ArrayList will return sorted results.

ArrayList list = GetList();
Developer.SortingBy = (SortFilter)Enum.Parse(typeof(SortFilter), "FirstName");
list.Sort();

Sorting Order

Now you may also want to add a sorting order feature  (ascending or descending) to your object. To support this, I add a SortOrder enumeartion.

public enum SortOrder
{
    Ascending,
    Descending
}

I also add SortingOrder static property to the Developer class as follows.

private static SortOrder sortingOrder = SortOrder.Ascending;
public static SortOrder SortingOrder
{
    get { return sortingOrder; }
    set { sortingOrder = value; }
}

I change the CompareTo method as follows. As you can see from this method, now I am checking if SortingOrder is Ascending or Descending, and based on that, I am comparing the values.

public int CompareTo(object obj)
{
    if (!(obj is Developer))
        throw new InvalidCastException("Not a valid Developer object.");
    Developer developer = (Developer)obj;
    switch (Developer.SortingBy)
    {
        case SortFilter.FirstName:
            if (Developer.SortingOrder == SortOrder.Ascending)
                return this.FirstName.CompareTo(developer.FirstName);
            else
                return developer.FirstName.CompareTo(this.FirstName);
        case SortFilter.LastName:
            if (Developer.SortingOrder == SortOrder.Ascending)
                return this.LastName.CompareTo(developer.LastName);
            else
                return developer.LastName.CompareTo(this.LastName);
        case SortFilter.Age:
            if (Developer.SortingOrder == SortOrder.Ascending)
                return this.Age.CompareTo(developer.Age);
            else
                return developer.Age.CompareTo(this.Age);
        case SortFilter.Experience:
            if (Developer.SortingOrder == SortOrder.Ascending)
                return this.Experience.CompareTo(developer.Experience);
            else
                return developer.Experience.CompareTo(this.Experience);
        default:
            goto case SortFilter.FirstName;
    }
}

Now the only thing I have to do is, set the SortingOrder property of the Developer object and call the Sort method. That should do it. See the attached source code for more details.

Developer.SortingOrder = (SortOrder)Enum.Parse(typeof(SortOrder), "Descending");
list.Sort();

Summary

In this article, I discussed how to use the IComparable interface to design a collection of sortable objects.


Recommended Free Ebook
Similar Articles
Mindcracker
Founded in 2003, Mindcracker is the authority in custom software development and innovation. We put best practices into action. We deliver solutions based on consumer and industry analysis.