The short answer is because a function Foo
can be implemented like this:
void Foo(IList<IShape> c)
{
c.Add(new Square());
}
If you passed a List<Circle>
to Foo
, the provided type would not be capable of storing the Square
, even though the type signatures claim it is okay. IList<T>
is not covariant: the general IList<Circle>
cannot be an IList<IShape>
because it cannot support the addition of arbitrary shapes.
The fix is to use IEnumerable<IShape>
to accept arguments in Foo
, but that won't work in all cases. IEnumerable<T>
is covariant: the specialized IEnumerable<Circle>
fits the contract of the general IEnumerable<IShape>
.
This behavior is also a Good Thing. The classic example of something that is covariant when it should not be is an array. The following code will compile, but will fail at runtime:
void Bar()
{
// legal in C#:
object[] o = new string[10];
// fails with ArrayTypeMismatchException: can't store Int in a String[]
o[0] = 10;
}