What is better to use for sorting IComparer or IComparable? Can you say which one is better to use and why? Personally, I myself did not quite understand which of them is better.

3 answers 3

In .NET , collection classes and arrays support sorting using one method, which is usually called Sort() However, the Sort() method by default only works for primitive type sets like int or string . To sort sets of complex objects, use the IComparable interface:

 public interface IComparable { int CompareTo(object o); } 

The CompareTo method on output returns an integer that can have one of three values:

  • Less than zero. This means that the current object must be in front of the object, which is passed as a parameter
  • It is zero. So both objects are equal
  • Above zero. This means that the current object must be after the object that is passed as a parameter.

IComparable has a generic version of IComparable<T> .
IComparable<T> , if defined for T , allows you to compare the current instance with another instance of the same type.

IComparable<T> comparable, and IComparer<T> comparator.

In addition to the IComparable interface IComparable the .NET platform also provides the IComparer interface:

 interface IComparer { int Compare(object o1, object o2); } 

The Compare method is designed to compare two objects o1 и o2 . It also returns three values, depending on the result of the comparison: If the first object is greater than the second,

  • then a number greater than 0 is returned.
  • if less then the number is less than zero
  • if both objects are equal, zero is returned

IComparer has a generic version of IComparer<T> .
IComparer<T> can be useful when you need to sort based on user order, but not as a general rule. For example, in the Person class at some point you may need to sort people according to their age. In this case, you can

 public class Person { public int Age; } public class AgeComparer : IComparer<Person> { public int Compare(Person x, Person y) { return x.Age - y.Age; } } 

Usually you need IComparable<T> . Ideally, you can only have one IComparable<T> , whereas several IComparer<T> possible based on different criteria.

    It depends on what the real meaning of your data is.

    If your data structure has a natural comparison operation, then you should implement IComparable<T> (there is almost no reason to use an untyped IComparable ), while the sorting will work as it should.

    For example, for the Rational data structure, which is a natural fraction, the comparison operation is obvious:

     class Rational : IComparable<Rational> { public int Numerator { get; } public int Denominator { get; } public int CompareTo(Rational that) { var result = Math.Sign((this.Numerator * that.Denominator) .CompareTo(this.Denominator * that.Numerator)); if (Math.Sign(this.Denominator) != Math.Sign(that.Denominator)) result = -result; return result; } } 

    Then the meaning of sorting is clear: you sort your objects in their natural order.

    If your objects do not have a natural order, then you have no reason to implement IComparable<T> .

    For example, if you have a Human object, it has no natural order. Sometimes you may want to sort a collection of people, sometimes by height, sometimes by last name, sometimes by date of birth. In this case, for sorting, you either specify how to calculate the “weight” of each object (as in the LINQ orderBy), or specify how to compare two elements ( IComparer<T> class or simply Comparison<T> lambda function). The easiest way is probably through OrderBy :

     humans.OrderBy(h => h.Surname).ToList() 

    A little harder through Comparison<T> :

     humans.Sort((p, q) => { var n1 = p.Surname; var n2 = q.Surname; return (n1 > n2) ? 1 : (n1 < b2) ? -1 : 0; }); 

    or simply

     humans.Sort((p, q) => p.Surname.CompareTo(q.Surname)); 

    The same can be done through the implementation of the IComparer<T> interface:

     class SurnameComparer : IComparer<Human> { public int Compare(Human p, Human q) { return p.Surname.CompareTo(q.Surname); } } 
     var cmp = new SurnameComparer; humans.Sort(cmp); 

    - but this path is usually the longest, and not so often needed: for example, when you need to reuse this “comparator”.

      These are two different variations of the same process.

      1. The IComparable interface allows an object to be "compared" with another, which allows the sorting method to sort collections of objects without thinking about what kind of objects they are. Those. the object itself knows what its "status" is to other objects. For example, there is an Email object with an email field and a Name field with a name field, and there is a single list of Email и Name . Emai l compares email.toString() and o.toString() and Name respectively, with the name . As a result, if there is a situation to sort this common list, it is sorted correctly, in accordance with the essence of the objects.

      2. The IComparer interface allows IComparer to implement your own sorting option, for a specific case, taking into account the specific situation, and not the essence of the classes. For example, you can implement two variants of ByNameCompare r and ByEmailComparer based on this interface. And apply them depending on the situation, to one collection - in one case by name, in the other - by email.