2011-02-02 1 views
0

Слон: Животное ....IList <T> vs IEnumerable <T>: Почему это присвоение недействительно?

IList<Elephant> elephants = new List<Elephant>(); 
IEnumerable<Animal> animalList = elephants; 

это работает хорошо, но ..

IList<Elephant> elephants = new List<Elephant>(); 
IList<Animal> animalList = elephants; 

выдает ошибку. Почему это?

+0

Возможный дубликат [Вопрос о C# ковариации] (http: // stackoverflow.com/questions/4034495/question-about-c-sharp-covariance) – nawfal

ответ

11

Если

IList<Elephant> elephants = new List<Elephant>(); 
IList<Animal> animalList = elephants; 

было возможно вы могли бы сделать это

animalList.Add(new Animal()); 

но animalList действительно ссылкой на то же referrent как elephants. И так как elephants - это IList<Elephant>, вы должны добавить экземпляр Animal в коллекцию, которая может содержать только экземпляры Elephant.

Причина, по которой это возможно для IEnumerable<T> является то, что IEnumerable<T> находится в параметре типа Tcovariant. Это связано с тем, что T отображается только в «выходных» позициях в интерфейсе IEnumerable<T>. Поскольку вы потребляете только экземпляры T из IEnumerable<T>, можно присвоить экземпляры IEnumerable<Derived> переменным типа IEnumerable<Base>. Все, что приходит из IEnumerable<Derived>, является Base, и именно поэтому что-то, что реализует IEnumerable<Derived>, можно использовать как безопасное. Вот почему безопасно назначать экземпляр IEnumerable<Elephant> переменной типа IEnumerable<Animal>.

Но у вас возникают проблемы с IList<T>, потому что IList<T> не является ковариантным параметром типа T. Проблема здесь в том, что T появляется в позициях «in» в интерфейсе IList<T>. Поэтому, если вы можете назначить экземпляры IList<Derived> переменным типа IList<Base>, вы можете попытаться привязать экземпляры Base к IList<Base>, которые действительно пытались бы вставить экземпляры Base в экземпляр IList<Derived>, и это явно небезопасно. Это именно та проблема, с которой мы столкнулись только с IList<Elephant> и IList<Animal>.

Обратите внимание, что IList<T> также не является контравариантным параметром типа T. Это связано с тем, что T появляется в «выходных» позициях в интерфейсе IList<T>. Если вы можете назначить экземпляры IList<Base> переменной типа IList<Derived>, вы можете попытаться захватить экземпляр Derived из IList<Derived>, который попытался бы захватить экземпляр Derived из экземпляра IList<Base>, и это явно абсурдно.

+0

Обратите также внимание, что 'Derived []' может использоваться как 'Base []', а вставка 'Derived' генерирует исключение. Это связано с тем, что .NET наследует нарушенные правила Java вокруг дисперсии массива. – porges

1

Это называется covariance и контравариантность.

Я не буду вдаваться в детали, но вы можете read about it.

 Смежные вопросы

  • Нет связанных вопросов^_^