Краткий ответ
Создавая экземпляр BindingList<SubBob>
, вы ограничиваете его работу с SubBob
и более конкретными типами (например, SubSubBob
).
Если вы хотите, чтобы Bob
также поместился там, объявите myList
как список наименее определенного типа, который вы хотите поддерживать :
BindingList<Bob> myList = new BindingList<Bob>();
(или, что более удобно,)
var myList = new BindingList<Bob>();
Объяснение
Речь идет не о памяти (BindingList
будет содержать только ссылку на объект, и все ссылки имеют одинаковый размер), скорее речь идет о логической несогласованности, которую вы привели бы.
Если бы такой код был возможен, вы бы могли произвольно нарушить ограничения типа :
BindingList<Animal> myList = new BindingList<Cat>();
myList.Add(new Dog()); // bang!
myList
- это список Cat
с, как вы ожидаете, что он будет обрабатывать Dog
?
Компилятор не узнает о проблеме и с радостью скомпилирует ваш код. Что должно произойти, когда этот код выполняется? Исключение? Но дженерики были введены именно для решения проблемы безопасности типов.
Дополнительная информация о совместной и контравариантности
Правильно, что в .NET 4.0, общая ковариация и контравариантность были добавлены для делегатов и интерфейсов ( не для классов ). Например, IEnumerable<out T>
является ковариантным, что означает, что вы можете присвоить его переменной любого типа менее производной , чем T
:
IEnumerable<Cat> cats = new List<Cat> { new Cat("Hudson"), new Cat("Crookshanks") };
IEnumerable<Animal> animals = cats; // sequence of cats is sequence of animals
Но это возможно только потому, что IEnumerable<out T>
гарантирует, что возвращает только T
(ключевое слово out
), а никогда не принимает . Если бы он принял T
в качестве параметра, он открыл бы проблему, описанную выше. По этой причине ICollection
является , а не ковариантным.
Аналогичным образом, некоторые интерфейсы гарантируют, что они принимают только T
(ключевое слово in
) и никогда не возвращают его. Такие интерфейсы называются контравариантными и позволяют присваивать переменную с более конкретно T
:
IComparer<Animal> animalComparer = // ...
IComparer<Dog> dogComparer = animalComparer; // comparer of animals is comparer of dogs