This questions involves 2 different implementations of essentially the same code.
First, using delegate to create a Comparison method that can be used as a parameter when sorting a collection of objects:
class Foo
{
public static Comparison<Foo> BarComparison = delegate(Foo foo1, Foo foo2)
{
return foo1.Bar.CompareTo(foo2.Bar);
};
}
I use the above when I want to have a way of sorting a collection of Foo objects in a different way than my CompareTo function offers. For example:
List<Foo> fooList = new List<Foo>();
fooList.Sort(BarComparison);
Second, using IComparer:
public class BarComparer : IComparer<Foo>
{
public int Compare(Foo foo1, Foo foo2)
{
return foo1.Bar.CompareTo(foo2.Bar);
}
}
I use the above when I want to do a binary search for a Foo object in a collection of Foo objects. For example:
BarComparer comparer = new BarComparer();
List<Foo> fooList = new List<Foo>();
Foo foo = new Foo();
int index = fooList.BinarySearch(foo, comparer);
My questions are:
Probably the biggest advantage to accepting a Comparison<T>
as opposed to an IComparer<T>
is the ability to write anonymous methods. If I have, let's say, a List<MyClass>
, where MyClass
contains an ID
property that should be used for sorting, I can write:
myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID));
Which is a lot more convenient than having to write an entire IComparer<MyClass>
implementation.
I'm not sure that accepting an IComparer<T>
really has any major advantages, except for compatibility with legacy code (including .NET Framework classes). The Comparer<T>.Default
property is only really useful for primitive types; everything else usually requires extra work to code against.
To avoid code duplication when I need to work with IComparer<T>
, one thing I usually do is create a generic comparer, like this:
public class AnonymousComparer<T> : IComparer<T>
{
private Comparison<T> comparison;
public AnonymousComparer(Comparison<T> comparison)
{
if (comparison == null)
throw new ArgumentNullException("comparison");
this.comparison = comparison;
}
public int Compare(T x, T y)
{
return comparison(x, y);
}
}
This allows writing code such as:
myList.BinarySearch(item,
new AnonymousComparer<MyClass>(x.ID.CompareTo(y.ID)));
It's not exactly pretty, but it saves some time.
Another useful class I have is this one:
public class PropertyComparer<T, TProp> : IComparer<T>
where TProp : IComparable
{
private Func<T, TProp> func;
public PropertyComparer(Func<T, TProp> func)
{
if (func == null)
throw new ArgumentNullException("func");
this.func = func;
}
public int Compare(T x, T y)
{
TProp px = func(x);
TProp py = func(y);
return px.CompareTo(py);
}
}
Which you can write code designed for IComparer<T>
as:
myList.BinarySearch(item, new PropertyComparer<MyClass, int>(c => c.ID));