No covariance for value type

For a while now I’ve been won­der­ing why C#‘s sup­port for covari­ance does not cov­er val­ue types, both in nor­mal array covari­ance and covari­ance in the gener­ic para­me­ter intro­duced in C# 4:

   1: void Main()

   2: {

   3:     int i = 0;

   4:     string str = "hello world";

   5:     

   6:     TestMethod(i);       // legal

   7:     TestMethod(str);     // legal

   8:     TestMethod2(Enumerable.Empty<int>());           // illegal

   9:     TestMethod2(Enumerable.Empty<string>());        // legal

  10:     

  11:     Console.WriteLine(i is object);                 // true

  12:     Console.WriteLine(new int[0] is object[]);      // false

  13:     Console.WriteLine(new string[0] is object[]);   // true

  14:     Console.WriteLine(new uint[0] is int[]);        // false

  15: }

  16:  

  17: public void TestMethod(object obj)

  18: {

  19:     Console.WriteLine(obj);

  20: }

  21:  

  22: public void TestMethod2(IEnumerable<object> objs)

  23: {

  24:     Console.WriteLine(objs.Count());

  25: }

Until I stum­bled upon this old post by Eric Lip­pert on the top­ic of array covari­ance, which essen­tial­ly points to a dis­agree­ment in the C# and CLI spec­i­fi­ca­tion on the rule of array covari­ance:

CLI

if X is assign­ment com­pat­i­ble with Y then X[] is assign­ment com­pat­i­ble with Y[]

C#

if X is a ref­er­ence type implic­it­ly con­vert­ible to ref­er­ence type Y then X[] is implic­it­ly con­vert­ible to Y[]

Whilst this doesn’t direct­ly point to the gener­ics case with IEnumerable<out T>, one would expect they are one and the same, oth­er­wise you end up with dif­fer­ent rules for int[] and IEnumerable<int> where (new int[0] is IEnumerable<int>) == true.. now that would be weird!

References:

Eric Lip­pert – Why is covari­ance of val­ue-typed arrays incon­sis­tent?

Ques­tion on Stack­Over­flow – why does my C# array lose type sign infor­ma­tion when cast to object?