C# note: Comparer versus Comparison

The Array.Sort method has a generic overload, Sort<T>(T[], Comparison<T>), that sorts the array using the passed comparison delegate to do item comparisons.

Array.Sort has another overload, Sort<T>(T[], IComparer<T>), that accepts a reference to an object that implements the IComparer<T> interface.

There are some non-generic array sorting functions, too. I won’t be covering those.

To call the first sorting function shown above, you have to create a Comparison<T> delegate. That’s easy enough:

// Create a descending item comparer
var descendingComparison =
    new Comparison<int>((i1,i2) => i2.CompareTo(i1));

For the second one, you need a reference to an object that implements the IComparer<T> interface. It’s possible to create an IComparer<t> from a Comparison<T>, like this:

var descendingComparer =
    new Comparer<int>.Create(descendingComparison);

Given the above, the code below shows how to call those functions.

    var a = new[] { 5, 5, 6, 9, 5, 3, 7, 7, 1, 5 };

    // Sort passing an IComparer<T>
    Array.Sort(a, descendingComparer);

    // Sort, passing Comparison<T>
    Array.Sort(a, descendingComparison);

Those two function calls do the same thing.

Array.Sort also has a generic overload that lets you sort portions of an array. That’s a pretty common thing to do, so it’s a nice function to have. But there’s only one function: Sort<T>(T[], Int32, Int32, IComparer<T>).

So if I’m sorting the entire array, I can pass a Comparison<T> or an IComparer<T>. But if I want to sort only part of the array, I have to pass an IComparer<T>?

That’s just idiotic! The existence of the overload that accepts a Comparison<T> to sort the entire array creates the expectation of a method that accepts a Comparison<T> to sort part of the array. That such a function doesn’t exist is, in my mind, an interface design error.

It might be that the overload that takes a Comparison<T> exists solely to support something in LINQ. The concept of sorting part of an array doesn’t really make sense in LINQ, so LINQ wouldn’t require a range sorting function. But the expectation is there on the human side, and the IDE’s error message when you write code to call the non-existent function is not at all helpful. When presented with this line:

Array.Sort(a, 0, 12, descendingComparer);

Visual Studio non-helpfully tells me:

Error (active) CS1503 Argument 2: cannot convert from 'int' to 'System.Array?'

Error (active) CS1503 Argument 4: cannot convert from 'System.Comparison' to 'int'

I guess figuring out which Sort method I’m trying to call is difficult. Overload resolution is hard enough when all the parameters match up. When one of the parameters is incorrect and there are multiple overloads that accept four parameters, the compiler has to guess at which one I was trying to call. That sounds like a hard problem.

If you’re unfamiliar with the difference between Comparison and Comparer, the difference is going to frustrate you.

Fortunately, the fix is easy. I showed above how to create an IComparer<T> from a Comparison<T>:

var descendingComparer =
    new Comparer<int>.Create(descendingComparison);

Or, if you don’t want to declare a new variable for it:

Array.Sort(a, 0, a.Length/2, new Comparer<int>.Create(descendingComparison));

You can go the other way, too, if you want. That is, to obtain a Comparison<T> from an IComparer<T>:

Comparison<int> descendingComparison = descendingComparer.Compare;

It looks to me as though Comparison<T> is not widely used in the .NET Framework. The only place I’ve seen it is in overloads to the Array.Sort and List.Sort methods. Comparer<T>, on the other hand, is used all over the place. In my code, I make an effort to keep a Comparer<T> around and create a Comparison<T> from it in the few cases that I need one.