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;
}
}
}
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 Circle
s, 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>
.