Может кто-нибудь объяснить, почему эта операция недействительна?

Сегодня я читал о ковариации и контравариантности и натолкнулся на статью об обмене стека, где Джон Скит объяснял инвариантность на уровне класса. Он использовал пример с фруктами, и почему допустить ковариацию на этом уровне было бы плохо:

//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, что явно плохо.

Другие вопросы по тегам