Это очень часто задаваемый вопрос. Давайте переименуем ваши типы:
abstract class Fruit { } // was BaseViewPresenter
abstract class FruitBowl where T : Fruit // was BaseView
class Apple : Fruit { } // was LoginPresenter
class BowlOfApples : FruitBowl { } // was LoginView
Теперь ваш вопрос:
У меня есть
blockquote>BowlOfApples
, который наследуется отFruitBowl
. Почему я не могу использовать его какFruitBowl
? Яблоко - это плод, поэтому чаша с яблоками - это миска с фруктами.Нет, это не так. Вы можете положить банан в миску с фруктами, но вы не можете положить банан в миску с яблоками, и поэтому чаша с яблоками - это не чаша с фруктами. (И по аналогичным соображениям, миска с фруктами также не является чашей из яблок.) Поскольку операции, которые вы можете законно выполнять на двух типах, отличаются , они не могут быть совместимыми .
Вот фотография легенды StackOverflow Jon Skeet, демонстрирующая этот факт:
[/g6]
Вы хотите, чтобы функция общая контравариантность , и поддерживается только на интерфейсах интерфейсов и делегатов , когда компилятор может доказать, что дисперсия безопасна, а когда переменный тип является ссылкой тип. Например, вы можете использовать
IEnumerable
в контексте, где требуетсяIEnumerable
, потому что компилятор может проверить, что вы не можете поместитьBanana
в последовательность фруктов.Проведите поиск по «ковариации и ковариации C #» на этом сайте или в Интернете, и вы найдете гораздо больше информации о том, как работает эта функция. В частности, моя серия статей о том, как мы разработали и реализовали эту функцию на C # 4, начинается здесь: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and- контрвариантность-в-с-часть-one.aspx