Introduction
Before we begin to understand these 2 interfaces, let's first understand the concept of sorting.
Say we have a list of integers & we want to sort them in ascending order. To achieve this we can use the direct sort method.
- List<int> listOfIntegaers = new List<int>() { 55, 12, 5, 78, 9, 65, 35, 5, 80, 8, 14 };
- listOfIntegaers.Sort();
- foreach (var item in listOfIntegaers)
- {
- System.Console.WriteLine(item + " ");
- }
Output
This list is sorted because sort method knows it has to sort on an integer number, which is a primitive datatype in C#.
IComparable
What if I want to sort user-defined type, say user-defined class? Let's try that.
First, create a user-defined class SmartPhone.
Note: I am overriding ToString() method of the object class to print output in as per my method.
- public class SmartPhone : IComparable
- {
- public string Name { get; set; }
- public double Price { get; set; }
- public string OS { get; set; }
- public bool IsFlagship { get; set; }
-
- public override string ToString()
- {
- return "Name: " + Name + "||"
- + " Price: " + Price + "||"
- + " OS: " + OS + "||"
- + " Is flagship Phone: " + IsFlagship;
- }
- }
Perfect, the next step is to create a List of this object and try to use a sort function.
- class Program
- {
- static void Main(string[] args)
- {
-
- List<SmartPhone> smartPhones = new List<SmartPhone>()
- {
- new SmartPhone()
- {
- Name = "One Plus 8",
- IsFlagship = true,
- OS = "Android 10",
- Price = 55000
- },
- new SmartPhone()
- {
- Name = "IPhone 11",
- IsFlagship = true,
- OS = "IOS 11",
- Price = 75000
- },
- new SmartPhone()
- {
- Name = "Samsung Note 10",
- IsFlagship = true,
- OS = "Android 10",
- Price = 110000
- },
- new SmartPhone()
- {
- Name = "Samsung S20 Ultra",
- IsFlagship = true,
- OS = "Android 10",
- Price = 130000
- },
- new SmartPhone()
- {
- Name = "IPhone 11 Pro",
- IsFlagship = true,
- OS = "IOS 11",
- Price = 130000
- }
- };
- smartPhones.Sort();
- foreach (var item in smartPhones)
- {
- System.Console.WriteLine(item.ToString());
- }
- }
- }
Now run the program.
Oops
, we encountered an error. It says the user-defined class SmartPhone has to implement interface IComparable.
Ok, let's do that.
- public class SmartPhone : IComparable
- {
- public string Name { get; set; }
- public double Price { get; set; }
- public string OS { get; set; }
- public bool IsFlagship { get; set; }
-
- public int CompareTo(object obj)
- {
- if (obj == null) return 1;
- SmartPhone nextSmartPhone = obj as SmartPhone;
- if(nextSmartPhone != null)
- {
- return this.Price.CompareTo(nextSmartPhone.Price);
- }
- else
- {
- throw new ArgumentException("Object doesn't have a proper price");
- }
- }
-
- public override string ToString()
- {
- return "Name: " + Name + "||"
- + " Price: " + Price + "||"
- + " OS: " + OS + "||"
- + " Is flagship Phone: " + IsFlagship;
- }
- }
We have to override ToCompare method. It takes one argument, the object type. So it compares the current object with the object next inline; e.g., to sort the list of integers sort method does bubble sort on elements.
- List: 10, 5, 25, 20
- Compare current item 10 with next 5 if (current > next): current is a large number; else next is a large number. In this case current is a large number so it swaps
- Now list is 5, 10, 25, 20. It will bubble up elements until it reaches the end of the list.
- End result: 5, 10, 20, 25
So as you can see ToCompare method returns an int. So if the current value is bigger than the next value it returns 1, else -1. And if both of them are the same then it returns 0
- 1: swap
- 0: retain
- -1: don't swap
What we are achieving in the above code is to sort the list of SmartPhones based on their price.
So:
- if the current price is bigger then next -> return 1
- if the current price is less then next -> return -1
- if both are same -> return 0.
Let's check out the output:
Sorted beautifully.
IComparer
This is how you can achieve sorting on user-defined classes with the help of the IComparable interface.
IComparable is going to help until you have complete control of user-defined class, but let's say you want to apply sorting on a class on which you don't have control. Meaning you can't change the implementation of a class; e.g., when you access some class from DLL, you can use them but you can't change their implementation.
In order to achieve sorting on such classes, the IComparer interface was born.
Let's understand this with an example.
For Apple, say display was supplied by Sony, which they can assemble in iPhone but Apple can't change the implementation of display..
The class display will have the following properties:
- PPI: Pixel per inch
- Resolution
- Size: in inch
- public class Display
-
- public int PPI { get; set; }
- public string Resolution { get; set; }
- public string Size { get; set; }
- }
Remember Apple cannot make changes in this class. So what Apple does is, they can make a new class, SortDisplay which is going to implement IComparer interface
- Sort displays as per PPI.
Next is SortDisplay class
- public class SortDisplay : IComparer<Display>
- {
- public int Compare(Display x, Display y)
- {
- Display firstDisplay = x as Display;
- Display secondDisplay = y as Display;
- return firstDisplay.PPI.CompareTo(secondDisplay.PPI);
- }
- }
- IComparable has a method named as CompareTo & has only 1 parameter. Because it compares the current object with the next object which is coming as a parameter. Hence current object CompareTo next object.
- But IComparer has 2 parameters because we are going to pass both of the objects as arguments. Hence method name is Compare: saying Compare the first parameter with the second parameter.
- While calling the sort function on the list, you have to pass an object of SortDisplay.
- class Program
- {
- static void Main(string[] args)
- {
- List<Display> displays = new List<Display>()
- {
- new Display()
- {
- PPI = 224,
- Resolution = "1080 * 1920",
- Size = "6.1"
- },
- new Display()
- {
- PPI = 300,
- Resolution = "1440 * 2180",
- Size = "7.1"
- },
- new Display()
- {
- PPI = 115,
- Resolution = "564 * 900",
- Size = "4.2"
- },
- new Display()
- {
- PPI = 160,
- Resolution = "880 * 980",
- Size = "5"
- }
- };
- SortDisplay sorted = new SortDisplay();
- displays.Sort(sorted);
- foreach (var item in displays)
- {
- System.Console.WriteLine("PPI: "+ item.PPI + " Resolution: "+ item.Resolution +" Size: "+ item.Size);
- }
- }
- }
Let's see the output.
There you go, displays are sorted as per PPI.
Conclusion
In this article, we learned:
- What are IComparable vs IComparer Interfaces & how to use them.
- A key difference between these two.
- When to use which one.
Thank you all.
Feel free to connect