The CLR doesn't support covariant return types, whereas it's supported delegate/interface generic variance from .NET 2.0 onwards.
In other words, it's not really up to the C# team, but the CLR team.
As to why the CLR doesn't support normal variance - I'm not sure, other than adding complexity, presumably without the requisite amount of perceived benefit.
EDIT: To counter the point about return type covariance, from section 8.10.4 of the CLI spec, talking about vtable "slots":
For each new member that is marked
"expect existing slot", look to see if
an exact match on kind (i.e., field or
method), name, and signature exists
and use that slot if it is found,
otherwise allocate a new slot.
From partition II, section 9.9:
Specifically, in order to determine
whether a member hides (for static or
instance members) or overrides (for
virtual methods) a member from a base
class or interface, simply substitute
each generic parameter with its
generic argument, and compare the
resulting member signatures.
There is no indication that the comparison is done in a way which allows for variance.
If you believe the CLR does allow for variance, I think that given the evidence above it's up to you to prove it with some appropriate IL.
EDIT: I've just tried it in IL, and it doesn't work. Compile this code:
using System;
public class Base
{
public virtual object Foo()
{
Console.WriteLine("Base.Foo");
return null;
}
}
public class Derived : Base
{
public override object Foo()
{
Console.WriteLine("Derived.Foo");
return null;
}
}
class Test
{
static void Main()
{
Base b = new Derived();
b.Foo();
}
}
Run it, with output:
Derived.Foo
Disassemble it:
ildasm Test.exe /out:Test.il
Edit Derived.Foo
to have a return type of "string" instead of "object":
.method public hidebysig virtual instance string Foo() cil managed
Rebuild:
ilasm /OUTPUT:Test.exe Test.il
Rerun it, with output:
Base.Foo
In other words, Derived.Foo no longer overrides Base.Foo as far as the CLR is concerned.