18

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.

nathanchere
  • 8,008
  • 15
  • 65
  • 86
JE42
  • 4,881
  • 6
  • 41
  • 51
  • 28
    That looks like a Mono bug, if anything. It's fine on .NET. – Jon Skeet May 14 '13 at 12:20
  • Could also be a Unity bug; I _think_ it does some compiling of its own when creating the various Unity files. (maybe not for C# though, perhaps only for its other supported languages) EDIT: Perhaps especially when targeting some platforms (like iOS) which is special and doesn't support all language features. – Chris Sinclair May 14 '13 at 12:29
  • 4
    Works fine on Mono 2.8 http://ideone.com/v785pl – ken2k May 14 '13 at 12:35
  • 1
    @JE42 I suggest posting this to the Unity3D devs/support; I'd bet money that this is an issue with the Unity compiler and/or runtime. – Chris Sinclair May 14 '13 at 12:42
  • Thanks ! I ll repost this in the unity forums. – JE42 May 14 '13 at 12:44
  • You should test with Mono 3.x, there's a big chance it may be already fixed. – knocte May 20 '13 at 17:30
  • 2
    Unity3d has the unfortunate problem that it is using a rather old mono version. I hope Unity3d will upgrade it sooner than later. – JE42 May 20 '13 at 20:50
  • 1
    Have you tried a[0] = (ITest)new Test(); – Alex Sep 13 '13 at 05:48
  • 1
    Cannot reproduce your problem - it complied and executed with C# with no error. – G.Y Oct 04 '13 at 01:28

1 Answers1

2

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.

Pharap
  • 3,826
  • 5
  • 37
  • 51