Author : Rahul
Last Modified : 02-Jun-2021
Complexity : Intermediate

IComparable and IComparer interface in C#


IComparable and IComparer both interfaces are used to sort the list of items. Before analysing these two interfaces first we try to understand the need for these interfaces.
There is one method named Sort() which is used to sort the collection of items. This method is worked very well with predefined data types.
Suppose we have a list of integers and we try to sort this list by using the Sort method.

 class Program
 {
   static void Main(string[] args)
   {
     List<int> numbers = new List<int>() { 5, 9, 1, 4, 8, 2, 6 };
     numbers.Sort();
     Console.WriteLine("List of numbers after sorting:");
     foreach(int item in numbers)
     {
       Console.WriteLine(item);
     }
     Console.Read();
   }
 }

Output

List of numbers after sorting:
1
2
4
5
6
8
9

So our Sort method working fine with predefined data types. Now we check whether the Sort method will work with user-defined data types or not.
Suppose we have a list of employees and we try to sort this list by using the Sort method.

 public class Employee
 {
   public string Emp_Name { get; set; }
   public string Emp_Department { get; set; }
   public int Emp_Salary { get; set; }
 }
 class Program
 {
   static void Main(string[] args)
   {
     List<Employee> employees = GetEmployeeList();
     employees.Sort();
     Console.WriteLine("Employee list after sorting:");
     foreach (Employee employee in employees)
     {
       Console.WriteLine($"Employee Name: {employee.Emp_Name}, Employee Department: {employee.Emp_Department}, " +
         $"Employee Salary: {employee.Emp_Salary}");
     }
     Console.Read();
   }
   /// <summary>
   /// Method to get employee list
   /// </summary>
   /// <returns></returns>
   private static List<Employee> GetEmployeeList()
   {
     List<Employee> employees = new List<Employee>();
     employees.Add(new Employee() { Emp_Name = "Rahul", Emp_Salary = 70000, Emp_Department = "IT" });
     employees.Add(new Employee() { Emp_Name = "Amit", Emp_Salary = 20000, Emp_Department = "HR" });
     employees.Add(new Employee() { Emp_Name = "Suresh", Emp_Salary = 40000, Emp_Department = "IT" });
     employees.Add(new Employee() { Emp_Name = "Nitish", Emp_Salary = 80000, Emp_Department = "Account" });
     return employees;
   }
 }

Output

System.InvalidOperationException: 'Failed to compare two elements in the array.'
Inner Exception
ArgumentException: At least one object must implement IComparable.

So Sort method is not worked with user-defined data types. To make the Sort method worked with user-defined data types, we need to override the Sort method for the Employee class. 
For this purpose, IComparable and IComparer interfaces are used. Now we try to understand both interfaces one by one.

IComparable

IComparable interface is used to compare two objects. This interface contains the CompareTo method, which we need to implement in our class. We have to implement custom sorting for Employee object within this method.

 public class Employee : IComparable
 {
   public string Emp_Name { get; set; }
   public string Emp_Department { get; set; }
   public int Emp_Salary { get; set; }
   //This method return int value based on comparision
   //If current value is greater than next value, it return 1 (swap both object)
   //If current value is smaller than next value, it return -1 (do not swap)
   //If current value and next value are same, it return 0 (do nothing)
   public int CompareTo(object obj)
   {
     if(obj == null)
     {
       return 1;
     }
     else
     {
       Employee nextEmployeeObj = obj as Employee;
       if(nextEmployeeObj != null)
       {
         return Emp_Name.CompareTo(nextEmployeeObj.Emp_Name);  //Here we done sorting based on employee name
       }
       else
       {
         throw new ArgumentException("Object is not proper.");
       }
     }
   }
 }
 class Program
 {
   static void Main(string[] args)
   {
     List<Employee> employees = GetEmployeeList();
     employees.Sort();
     Console.WriteLine("Employee list after sorting:");
     foreach (Employee employee in employees)
     {
       Console.WriteLine($"Employee Name: {employee.Emp_Name}, Employee Department: {employee.Emp_Department}, " +
         $"Employee Salary: {employee.Emp_Salary}");
     }
     Console.Read();
   }
   /// <summary>
   /// Method to get employee list
   /// </summary>
   /// <returns></returns>
   private static List<Employee> GetEmployeeList()
   {
     List<Employee> employees = new List<Employee>();
     employees.Add(new Employee() { Emp_Name = "Rahul", Emp_Salary = 70000, Emp_Department = "IT" });
     employees.Add(new Employee() { Emp_Name = "Amit", Emp_Salary = 20000, Emp_Department = "HR" });
     employees.Add(new Employee() { Emp_Name = "Suresh", Emp_Salary = 40000, Emp_Department = "IT" });
     employees.Add(new Employee() { Emp_Name = "Nitish", Emp_Salary = 80000, Emp_Department = "Account" });
     return employees;
   }
 }

Output

Employee list after sorting:
Employee Name: Amit, Employee Department: HR, Employee Salary: 20000
Employee Name: Nitish, Employee Department: Account, Employee Salary: 80000
Employee Name: Rahul, Employee Department: IT, Employee Salary: 70000
Employee Name: Suresh, Employee Department: IT, Employee Salary: 40000

 

IComparer

Sorting on user-defined data types are done very easily with the IComparable interface but there is one problem in the IComparable interface. IComparable interface needs to update user-defined classes for implementing sorting functionality. Think that, if we want to sort the list of user-defined classes and this class is a third class library and we can not update. Meaning that we can not change the implementation of that class. In this case, the IComparable interface can not be used, here the IComparer interface will be used.

 public class Employee
 {
   public string Emp_Name { get; set; }
   public string Emp_Department { get; set; }
   public int Emp_Salary { get; set; }
 }
 //This class is used to sort employee by name
 public class SortEmployeeByName : IComparer<Employee>
 {
   //This method return int value based on comparision
   //If current value is greater than next value, it return 1 (swap both object)
   //If current value is smaller than next value, it return -1 (do not swap)
   //If current value and next value are same, it return 0 (do nothing)
   public int Compare(Employee x, Employee y)
   {
     return x.Emp_Name.CompareTo(y.Emp_Name);
   }
 }
 //This class is used to sort employee by salary
 public class SortEmployeeBySalary : IComparer<Employee>
 {
   //This method return int value based on comparision
   //If current value is greater than next value, it return 1 (swap both object)
   //If current value is smaller than next value, it return -1 (do not swap)
   //If current value and next value are same, it return 0 (do nothing)
   public int Compare(Employee x, Employee y)
   {
     return x.Emp_Salary.CompareTo(y.Emp_Salary);
   }
 }
 class Program
 {
   static void Main(string[] args)
   {
     List<Employee> employees = GetEmployeeList();
     SortEmployeeByName sortEmployeeByName = new SortEmployeeByName();
     employees.Sort(sortEmployeeByName);
     Console.WriteLine("Employee list after sorting (By Name):");
     foreach (Employee employee in employees)
     {
       Console.WriteLine($"Employee Name: {employee.Emp_Name}, Employee Department: {employee.Emp_Department}, " +
         $"Employee Salary: {employee.Emp_Salary}");
     }
     SortEmployeeBySalary sortEmployeeBySalary = new SortEmployeeBySalary();
     employees.Sort(sortEmployeeBySalary);
     Console.WriteLine("Employee list after sorting (By Salary):");
     foreach (Employee employee in employees)
     {
       Console.WriteLine($"Employee Name: {employee.Emp_Name}, Employee Department: {employee.Emp_Department}, " +
         $"Employee Salary: {employee.Emp_Salary}");
     }
     Console.Read();
   }
   /// <summary>
   /// Method to get employee list
   /// </summary>
   /// <returns></returns>
   private static List<Employee> GetEmployeeList()
   {
     List<Employee> employees = new List<Employee>();
     employees.Add(new Employee() { Emp_Name = "Rahul", Emp_Salary = 70000, Emp_Department = "IT" });
     employees.Add(new Employee() { Emp_Name = "Amit", Emp_Salary = 20000, Emp_Department = "HR" });
     employees.Add(new Employee() { Emp_Name = "Suresh", Emp_Salary = 40000, Emp_Department = "IT" });
     employees.Add(new Employee() { Emp_Name = "Nitish", Emp_Salary = 80000, Emp_Department = "Account" });
     return employees;
   }
 }

Output

Employee list after sorting (By Name):
Employee Name: Amit, Employee Department: HR, Employee Salary: 20000
Employee Name: Nitish, Employee Department: Account, Employee Salary: 80000
Employee Name: Rahul, Employee Department: IT, Employee Salary: 70000
Employee Name: Suresh, Employee Department: IT, Employee Salary: 40000
Employee list after sorting (By Salary):
Employee Name: Amit, Employee Department: HR, Employee Salary: 20000
Employee Name: Suresh, Employee Department: IT, Employee Salary: 40000
Employee Name: Rahul, Employee Department: IT, Employee Salary: 70000
Employee Name: Nitish, Employee Department: Account, Employee Salary: 80000