Если я позволю вашему List<Joe> joes
быть обобщенным как ...
List<Human> humans = joes;
... две ссылки humans
и joes
теперь указывают на один и тот же список. Код, следующий за вышеприведенным присвоением, не может предотвратить вставку / добавление экземпляр другого типа человека, скажем, водопроводчик, в список. Учитывая, что class Plumber: Human {}
humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe
список, на который ссылается humans
, теперь содержит как джо, так и сантехников. Обратите внимание, что на тот же объект списка все еще ссылается ссылка joes
. Теперь, если я воспользуюсь ссылкой joes
для чтения из объекта списка, я мог бы вывести водопроводчика вместо joe . Сантехник и Джо, как известно, неявно взаимопревращаемы ... поэтому мое получение водопроводчика вместо Джо из списка нарушает безопасность типов. Слесарь-сантехник, конечно, не приветствуется через ссылку на список joes.
Однако в последних версиях C # его можно обойти это ограничение для универсального класса / коллекции, реализовав универсальный интерфейс, параметр типа которого имеет модификатор out
. Скажем, у нас теперь есть ABag<T> : ICovariable<out T>
. Модификатор out ограничивает T только выходными позициями (например, тип возвращаемых методов). Вы не можете ввести T в сумку. Вы можете только прочитать их из этого. Это позволяет нам обобщать joes для ICovariable<Human>
, не беспокоясь о вставке в него сантехника, так как интерфейс этого не позволяет. Теперь мы можем написать ...
ICovariable<Human> humans = joes ; // now its good !
humans.Add(new Plumber()); // error