Search code examples
collections.net-2.0c#-2.0

IComparer<> and class inheritance in C#


Is there any way to implement specialized IComparer for the base class type so a child class could still use it for sorting in speciliazed collections?

Example

public class A
{
   public int X;
}

public class B:A
{
   public int Y;
}

public AComparer:IComparer<A>
{
    int Compare(A p1, A p2)
    {
        //...
    }

}

so following code will work:

List<A> aList = new List<A>();
aList.Sort(new AComparer());

List<B> bList = new List<B>();
bList.Sort(new AComparer()); // <- this line fails due to type cast issues 

How to approach this issue to have both - inheritance of sorting and specialized collections (and do not copy IComparer classes for each of children classes?

Thanks in advance!


Solution

  • Firstly, note that this is fixed in .NET 4 via generic contravariance - your code would simply work. EDIT: As noted in comments, generic variance was first supported in CLR v2, but various interfaces and delegates only became covariant or contravariant in .NET 4.

    However, in .NET 2 it's still fairly easy to create a converter:

    public class ComparerConverter<TBase, TChild> : IComparer<TChild>
        where TChild : TBase
    {
        private readonly IComparer<TBase> comparer;
    
        public ComparerConverter(IComparer<TBase> comparer)
        {
            this.comparer = comparer;
        }
    
        public int Compare(TChild x, TChild y)
        {
            return comparer.Compare(x, y);
        }
    }
    

    You can then use:

    List<B> bList = new List<B>();
    IComparer<B> bComparer = new ComparerConverter<A, B>(new AComparer());
    bList.Sort(bComparer);
    

    EDIT: There's nothing you can do without changing the way of calling it at all. You could potentially make your AComparer generic though:

    public class AComparer<T> : IComparer<T> where T : A
    {
        int Compare(T p1, T p2)
        {
            // You can still access members of A here
        }    
    }
    

    Then you could use:

    bList.Sort(new AComparer<B>());
    

    Of course, this means making all your comparer implementations generic, and it's somewhat ugly IMO.