Ковариантность нарушена в массивах C#?

Рассмотрим следующий общий интерфейс 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).

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

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();

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

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

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

Другие вопросы по тегам