Вопрос о ковариации С#

В приведенном ниже коде:

interface I1 { }
class CI1: I1 { }

List<CI1> listOfCI1 = new List<CI1>();

IEnumerable<I1> enumerableOfI1 = listOfCI1; //this works

IList<I1> listofI1 = listOfCI1; //this does not

Я могу присвоить свой "listOfCI1" IEnumerable<I1> (из-за ковариации)

Но почему я не могу назначить его IList<I1>? В этом случае я не могу даже сделать следующее:

List<I1> listOfI12 = listOfCI1;

Не следует ли ковариация разрешить присвоить производный тип базовому типу?

Ответы

Ответ 1

Проще говоря, IList<T> не является ковариантным, тогда как IEnumerable<T> is. Вот почему...

Предположим, что IList<T> ковариантно. Код ниже явно не безопасен для типов... но где вы хотите, чтобы ошибка была?

IList<Apple> apples = new List<Apple>();
IList<Fruit> fruitBasket = apples;
fruitBasket.Add(new Banana()); // Aargh! Added a Banana to a bunch of Apples!
Apple apple = apples[0]; // This should be okay, but wouldn't be

За подробностями о различиях см. Eric Lippert серия сообщений блога на нем или посмотрите видео из моего разговора об отклонениях от NDC.

В принципе, дисперсия разрешена только там, где она гарантирована как безопасная (и в режиме сохранения в виде, поэтому вы не можете преобразовать IEnumerable<int> в IEnumerable<object> - преобразование бокса не сохраняет представление).

Ответ 2

Сравнить объявления (msdn)

public interface IEnumerable<out T> : IEnumerable

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable

вы видите волшебное слово out? Это означает, что ковариация включена.

Ответ 3

Нет.

В противном случае вы могли бы добавить другую реализацию I1 в список, который должен содержать только C1 s.

Ответ 4

Интерфейс

IList<T> не является ковариантным.