Question

Earlier, I was provided a concrete example of contravariance in the generic IComparable<T> interface by Jon Skeet. This has, however, spawned yet another question. Why isn't the generic List<T>.Sort() method able to infer the same information?

I've provided the referenced example here as the static Foo<T>() method. You'll observe that this method is able to infer the type of T and call CompareTo(Circle). List<ICircle>.Sort() on the other hand is unable to infer the type of T and so calls CompareTo(Object).

using System;
using System.Collections.Generic;

namespace Testable
{
    public class Test
    {
        public static void Main()
        {
            List<ICircle> circles = new List<ICircle>();

            circles.Add(new Circle());
            circles.Add(new Circle());

            circles.Sort();

            Foo(new Circle(), new Circle());
        }

        public static void Foo<T>(IComparable<T> a, T b) where T : ICircle
        {
            a.CompareTo(b);
        }
    }

    public interface ICircle
    {
    }

    public class Circle :
    IComparable, IComparable<Circle>, ICircle
    {
        public Int32 CompareTo(Object other)
        {
            Console.WriteLine("Called CompareTo(Object)");
            return 0;
        }

        public Int32 CompareTo(Circle other)
        {
            Console.WriteLine("Called CompareTo(Circle)");
            return 0;
        }
    }
}
Was it helpful?

Solution

The list is a list of ICircle, not a list of Circle. ICircle doesn't implement IComparable<Circle> or IComparable<ICircle> (or any other IComparable interface. All of the items that you put into the list do happen to implement IComparer<Circle> and be Circles, but you could just as easily put an item in that implemented ICircle and implemented no IComparable interfaces at all.

Foo on the other hand takes an IComparable<T> as a parameter, so you know that it implements IComparable<T>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top