IComparable is a C# interface that allows a class to define its default comparison behavior. It is useful when objects need to be arranged according to a specific ordering rule.
- Defines the CompareTo() method for object comparison.
- Supports ordering of custom objects based on class-defined comparison logic.
- The generic IComparable<T> interface provides a type-safe alternative.
Example: Program to implements IComparable in a Student class. The CompareTo() method compares students using their Marks property.
using System;
class Student : IComparable
{
public string Name { get; set; }
public int Marks { get; set; }
public Student(string name, int marks)
{
Name = name;
Marks = marks;
}
public int CompareTo(object obj)
{
if (obj is not Student other)
throw new ArgumentException("Object is not a Student.");
return Marks.CompareTo(other.Marks);
}
}
class Program
{
static void Main()
{
Student[] students =
{
new Student("Rahul", 85),
new Student("Amit", 65),
new Student("Neha", 90)
};
Array.Sort(students);
foreach (Student student in students)
{
Console.WriteLine($"{student.Name}: {student.Marks}");
}
}
}
Output
Amit: 65 Rahul: 85 Neha: 90
Explanation: The Student class implements IComparable and compares objects using their Marks property. Array.Sort() calls CompareTo() to arrange the students in ascending order of marks.
Syntax
The IComparable interface belongs to the System namespace and declares the CompareTo() method.
public interface IComparable
{
int CompareTo(object? obj);
}
A class that implements IComparable must provide an implementation of CompareTo().
CompareTo() Method
CompareTo() contains the logic used to compare the current object with another object. The method receives the object being compared and returns an integer representing their ordering relationship.
Syntax
public int CompareTo(object? obj)
- obj is the object being compared with the current object.
- The comparison is based on the properties selected by the implementing class.
- The returned integer determines the relative order of the two objects.
Return Values of CompareTo()
The result of CompareTo() determines the position of the current object relative to the object being compared.
| Return Value | Meaning |
|---|---|
| Less than 0 | The current object comes before the specified object. |
| 0 | The current object is equal to the specified object. |
| Greater than 0 | The current object comes after the specified object. |
The exact integer value is not important; only whether the result is negative, zero, or positive matters.
Sorting Objects Using IComparable
Once a class implements IComparable, standard sorting methods can use its default comparison logic.
Array.Sort()
Array.Sort() sorts an array according to the ordering defined by CompareTo().
Array.Sort(students);
List<T>.Sort()
List<T>.Sort() can also use the default comparison implemented by the objects in the list.
List<Student> students = new List<Student>
{
new Student("Rahul", 85),
new Student("Amit", 65),
new Student("Neha", 90)
};students.Sort();
Both methods use the comparison logic provided by IComparable to determine the order of the objects.
IComparable vs IComparable<T>
IComparable and IComparable<T> both define default comparison behavior, but the generic version provides a strongly typed comparison method.
| Feature | IComparable | IComparable<T> |
|---|---|---|
| Namespace | System | System |
| Type | Non-generic | Generic |
| Method | CompareTo(object obj) | CompareTo(T other) |
| Type safety | Requires casting | Type-safe |
| Boxing | May occur for value types | Avoids unnecessary boxing |
| Performance | Generally less efficient | Generally more efficient |
| Recommended for new code | Less preferred | Generally preferred |
IComparable<T> Example
using System;
class Student : IComparable<Student>
{
public string Name { get; set; }
public int Marks { get; set; }
public Student(string name, int marks)
{
Name = name;
Marks = marks;
}
public int CompareTo(Student other)
{
if (other == null)
return 1;
return Marks.CompareTo(other.Marks);
}
}
Here, CompareTo() accepts a Student directly, so no conversion from object is required.
For new code, IComparable<T> is generally preferred because it provides type-safe comparisons and avoids the casting required by the non-generic interface.
Limitations of IComparable
Although IComparable is useful for default ordering, it is not suitable for every comparison requirement.
- A type normally has only one default ordering defined through IComparable.
- The non-generic interface requires working with object, which can require type checking or casting.
- Different ordering requirements are better handled with IComparer<T>.
- Poorly designed comparison logic can result in inconsistent sorting behavior.
Best Practices for IComparable
Follow these practices when implementing IComparable to make comparisons reliable and predictable.
- Base the comparison on properties that clearly represent the type's default ordering.
- Ensure comparison results are consistent for the same pair of objects.
- Handle incompatible object types appropriately when using non-generic IComparable.
- Prefer IComparable<T> for new implementations.
- Keep comparison logic focused and avoid changing object state inside CompareTo().
- Ensure the comparison behavior is consistent with equality when appropriate.