Подтвердить что ты не робот

Ковариация сломана в массивах С#?

Рассмотрим следующий общий интерфейс ITest с параметром ковариационного типа T, общий класс Test, реализующий интерфейс, и класс A и с подклассом 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 {    
}

Следующий код компилируется без ошибок, но генерирует исключение времени выполнения System.ArrayTypeMismatchException:

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

но этот код работает очень хорошо:

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

Это было проверено на Mono 2.10.2 (Unity3d 4.1). Я думаю, что это как-то связано с разбитой ковариацией в массивах (см. http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx).

Я не понимаю, почему проверка типа, которая происходит при назначении слота массива, не учитывает ковариацию.

4b9b3361

Ответ 1

Я скомпилировал и протестировал данный код в VS2010 с использованием .Net 4 в Windows 7, и он отлично работает, он не дает исключений во время выполнения, поэтому кажется, что ваша проблема связана либо с Mono, либо с Unity.

С данным кодом трудно сделать предположения о том, в чем проблема. Точный тип исключения и некоторые другие тестовые примеры (то есть варианты, которые не используют интерфейсы) помогут сузить точную проблему, но это вопрос сообщества сообщества Mono | Unity.

Что касается того, что он связан с этой статьей, он не связан.

В статье описывается следующая ситуация:

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

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

Чтобы упростить то, что Эрик говорит в своей статье:

a - это переменная, которая может содержать массив любого типа, который наследуется от  А.

a назначается массив из B, таким образом, a представляет собой массив из B.

Когда пользователь пытается назначить новый элемент C элементу a, существует тип  несоответствие, потому что a на самом деле является массивом B, поэтому присвоение C элементу a эквивалентно попытке назначить новый C переменной, которая содержит B следующим образом:

B b = new C();

Аналогичная проблема возникла бы при назначении a как массива C.

Однако, поскольку a определен как способный удерживать массив A, пользователь может назначить массив из A, который позволит ему принимать оба значения B и C.

Именно из-за этого код в вашем вопросе, похоже, связан с этой проблемой, но на самом деле это не потому, что он назначается как массив ITest, но он должен иметь возможность хранить типы ITest и что возникающая ошибка выполнения связана с ошибкой в ​​режиме исполнения Mono или Unity.