Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Covariance broken in C# arrays?

Consider following generic interface ITest with a covariant type parameter T, the generic class Test implementing the interface, and a class A and with a subclass B:

interface ITest<out T> 
{    
  T prop{ get;}
}
class Test<T> : ITest<T>
{    
    public T prop{ get {
       return default(T);    
    }}
}
class A {    
}
class B: A {    
}

The following code compiles with no errors but throws the runtime exception System.ArrayTypeMismatchException:

ITest<A>[] a = new ITest<A>[1];
a[0] = new Test<B>(); //<-- throws runtime exception

but this code works just fine:

ITest<A> r = new Test<B>();

This has be tested on Mono 2.10.2 (Unity3d 4.1). I think this somehow is related to the broken covariance in arrays (see http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx).

I am not clear why the type-check that is happening when the array slot is assigned is not taking covariance into account.

like image 458
JE42 Avatar asked May 14 '13 12:05

JE42


1 Answers

I have compiled and tested the given code in VS2010 using .Net 4 on Windows 7 and it works fine, it does not give a runtime exception, thus it would seem that your problem is either Mono or Unity related.

With the given code it is hard to make assumptions about what the problem is. The exact type of the exception and some other test cases (ie variations that do not use interfaces) would help narrow down the exact problem, but that is a matter for the Mono|Unity community to solve.

As for it being linked to that article, it is unrelated.

What the article is describing is the following situation:

class A { }
class B: A { }
class C: A { }

A[] a = new B[1];
a[0] = new C(); //<-- throws ArrayTypeMismatchException

To simplify what Eric is saying in his article:

a is a variable that can hold an array of any type that inherits from A.

a is assigned an array of B, thus a is an array of B.

When the user tries to assign a new C to an element of a, there is a type mismatch because a is actually an array of B, thus assigning a C to an element of a is equivalent to trying to assign a new C to a variable that holds B like so:

B b = new C();

A similar issue would arise from assigning a as an array of C.

However, as a is defined as being able to hold an array of A, the user could assign an array of A, which would allow it to accept both values of B and C.

It is because of this that the code in your question appears to be related to this problem, but in fact it is not because a is assigned as an array of ITest, meeaning it should be able to store types of ITest and that the runtime error being thrown stems from an error in either Mono or Unity's runtime.

like image 57
Pharap Avatar answered Sep 20 '22 22:09

Pharap