Sorting with IComparable in C#

In C#, the IComparable interface is used to define a generic way to compare objects. When a class implements this interface, its instances can be sorted and compared with other instances of the same class or other classes that implement the same interface.

So, let's start with an example.

var numbers = new List<int> { 5, 2, 3, 1, 4 };
numbers.Sort();
Console.Write("Sorted Numbers: ");
numbers.ForEach(number => Console.Write(number + " "));

Here, I have created a list of integers with values "5,2,3,1,4". Then the numbers list is sorted by calling the Sort method of the List class.

Sort method

The numbers are sorted in ascending order.

Now, let's sort out a list of complex types.

Step 1. Create a class named AppleProduct.

public class AppleProduct
{
    public string Name { get; init; }
    public int Price { get; init; }
}

Step 2. Create a List<AppleProduct> and initialize it with some values.

var appleProducts = new List<AppleProduct>
{
    new AppleProduct { Name = "iPad Pro", Price = 1199 },
    new AppleProduct { Name = "iPhone 16 Pro", Price = 999 },
    new AppleProduct { Name = "iPhone 16 Pro Max", Price = 1099 },
    new AppleProduct { Name = "iPhone 15 Pro", Price = 899 }
};

Step 3. Sort the collection and print it in the console.

Console.WriteLine("\n\nSorted Apple Products : ");
appleProducts.Sort();
appleProducts.ForEach(product =>
{
    Console.WriteLine($"{product.Name} : {product.Price}");
});

Step 4. Run the code.

 Run the code

The numbers collection is sorted in ascending order, but while sorting the appleProducts collection, there is an unhandled exception.

  • Failed to compare two elements in the array.
  • At least one object must implement IComparable.

Alright, so how'd the numbers get sorted?

To understand that let's see the definition of int.

public readonly struct Int32
    : IComparable,
      IConvertible,
      ISpanFormattable,
      IComparable<int>,
      IEquatable<int>,
      IBinaryInteger<int>,
      IMinMaxValue<int>,
      ISignedNumber<int>,
      IUtf8SpanFormattable,
      IBinaryIntegerParseAndFormatInfo<int>
{
}

The Int32 Struct extends the IComparable & IComparable<int>.

What is an IComparable interface?

The IComparable interface is used to define a default sort order for objects of a specific type. This means that classes that implement this interface can be sorted and ordered in a specific way.

The IComparable<T> interface looks like this

public interface IComparable<in T>
  {
    int CompareTo(T? other);
  }

The IComparable<T> interface defines a single abstract method, CompareTo, which takes an object of type T and returns an integer.

The Int32 has extended and implemented the IComparable<int>. The CompareTo method looks like

public int CompareTo(int value)
  { 
    if (m_value < value) return -1;
    if (m_value > value) return 1;
    return 0;
  }

In this context, m_value represents the instance of the class on which the CompareTo method is invoked. The value parameter holds the object that is being compared to the current instance.

  1. -1 means: The current instance is less than the object being compared to.
  2. 0 means: The current instance is equal to the object being compared to
  3. 1 means: The current instance is greater than the object being compared to.

The CompareTo logic is invoked internally by the List class's Sort method to determine the relative order of elements. The default sorting order is ascending.

Step 5. To enable the sorting of AppleProduct objects, the AppleProduct class needs to implement the IComparable<T> interface. This allows the sorting algorithm to compare AppleProduct instances and determine their relative order.

public class AppleProduct:IComparable<AppleProduct>
{
    public string Name { get; init; } 

    public int Price { get; init; }

    public int CompareTo(AppleProduct other)
    {
        //implement the comparison logic here
    }
}

The CompareTo method requires custom logic for comparison. Since the Price property is an int, we can directly utilize its built-in CompareTo method for integer comparison.

public class AppleProduct:IComparable<AppleProduct>
{
    public string Name { get; init; } 

    public int Price { get; init; }

    public int CompareTo(AppleProduct other)
    {
        return Price.CompareTo(other.Price);
    }
}

Step 6. Run the code

Code

This time, the Apple products collection is ordered in ascending order based on their respective prices.

Step 7. If we want to sort the Apple products collection by price in descending order, we need to invert the comparison logic used in the CompareTo method.

public class AppleProduct : IComparable<AppleProduct>
{
    public string Name { get; init; } 
    public int Price { get; init; }

    public int CompareTo(AppleProduct other)
    {
        return other.Price.CompareTo(Price);
    }
}

Step 8. Run the code.

Output

This time, the Apple products collection is sorted in descending order by price.

By understanding and effectively implementing the IComparable interface, you can enhance the sorting and comparison capabilities of your C# applications.

Happy Coding!


Similar Articles