Может кто-нибудь объяснить, почему эта операция недействительна?
Сегодня я читал о ковариации и контравариантности и натолкнулся на статью об обмене стека, где Джон Скит объяснял инвариантность на уровне класса. Он использовал пример с фруктами, и почему допустить ковариацию на этом уровне было бы плохо:
//Bad
List<Banana> bunchOfBananas = new List<Banana>();
// This would be valid if List<T> were covariant in T
List<Fruit> fruitBowl = bunchOfBananas;
fruitBowl.Add(new Apple());
Banana banana = bunchOfBananas[0];
Итак, как это объясняет наличие списка Fruit, в который вы бы добавили экземпляры класса, наследуемые от Fruit? Например:
//Good
List<Fruit> fruitBowl = new List<Fruit>();
fruitBowl.Add(new Apple());
fruitBowl.Add(new Banana());
Я делал это в прошлом, и он всегда ведет себя как ожидалось. Почему CLR не смотрит на тип fruitBowl? Это потому, что вы устанавливаете значение вашего fruitBowl для списка бананов, который сначала ковариантен списку фруктов, а затем пытаетесь добавить яблоко в коллекцию, тип которой действительно List<Banana>
?
Спасибо Мэтту ниже. Это помогает помнить, что вы имеете дело с ссылочными типами. Понизить это навсегда.
1 ответ
Я думаю, что вам не хватает того, что вы делаете в своем первом примере:
List<Fruit> fruitBowl = bunchOfBananas;
Вы не делаете копию bunchOfBananas
в List<Fruit>
, Вместо этого вы создаете ссылку на гроздь бананов, и эту ссылку можно использовать для добавления любого вида фруктов.
Таким образом, когда вы делаете:
fruitBowl.Add(new Apple());
вы бы не добавляли яблоко в список фруктов; вы бы добавили яблоко к List<Banana> bunchOfBananas
, что явно плохо.