Вопрос о ковариации С#
В приведенном ниже коде:
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>
не является ковариантным.