Ничего не помогло, пока я не нашел это решение: https://stackoverflow.com/a/39068538/3995091
В Android SDK инструменты сборки с правильной версией, где показанный как установленный, но все же я получил ту же ошибку, заявив, что их невозможно найти. Когда я использовал вышеупомянутое решение, я узнал, что они действительно не установлены, хотя Android SDK считали, что это так. Установка их решила для меня.
Это очень часто задаваемый вопрос. Давайте переименуем ваши типы:
abstract class Fruit { } // was BaseViewPresenter
abstract class FruitBowl<T> where T : Fruit // was BaseView
class Apple : Fruit { } // was LoginPresenter
class BowlOfApples : FruitBowl<Apple> { } // was LoginView
Теперь ваш вопрос:
У меня есть
blockquote>BowlOfApples
, который наследуется отFruitBowl<Apple>
. Почему я не могу использовать его какFruitBowl<Fruit>
? Яблоко - это плод, поэтому чаша с яблоками - это миска с фруктами.Нет, это не так. Вы можете положить банан в миску с фруктами, но вы не можете положить банан в миску с яблоками, и поэтому чаша с яблоками - это не чаша с фруктами. (И по аналогичным соображениям, миска с фруктами также не является чашей из яблок.) Поскольку операции, которые вы можете законно выполнять на двух типах, отличаются , они не могут быть совместимыми .
Вот фотография легенды StackOverflow Jon Skeet, демонстрирующая этот факт:
[/g6]
Вы хотите, чтобы функция общая контравариантность , и поддерживается только на интерфейсах интерфейсов и делегатов , когда компилятор может доказать, что дисперсия безопасна, а когда переменный тип является ссылкой тип. Например, вы можете использовать
IEnumerable<Apple>
в контексте, где требуетсяIEnumerable<Fruit>
, потому что компилятор может проверить, что вы не можете поместитьBanana
в последовательность фруктов.Проведите поиск по «ковариации и ковариации C #» на этом сайте или в Интернете, и вы найдете гораздо больше информации о том, как работает эта функция. В частности, моя серия статей о том, как мы разработали и реализовали эту функцию на C # 4, начинается здесь: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and- контрвариантность-в-с-часть-one.aspx
Я принял ответ Эрика, так как он дает отличное объяснение, почему то, что я хотел, было невозможно, но я также подумал, что поделюсь своим решением, если кто-то еще столкнется с этой проблемой.
Я удалил параметр generic type из моего исходного класса BaseView
и создал вторую версию класса BaseView
, которая включала в себя параметр типового типа и особенности для него.
Первая версия используется моим методом .Resolve()
или другим кодом, который не заботится о конкретных типах, а вторая версия используется любым кодом, который действительно заботится, например, имплантация BaseView
Вот пример того, как мой код закончил поиск
// base classes
public abstract class BaseViewPresenter { }
public abstract class BaseView : UserControl
{
public BaseViewPresenter Presenter { get; set; }
}
public abstract class BaseView<T> : BaseView
where T : BaseViewPresenter
{
public new T Presenter
{
get { return base.Presenter as T; }
set { base.Presenter = value; }
}
}
// specific classes
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter>
{
// Can now call things like Presenter.LoginPresenterMethod()
}
// updated .Resolve method used for obtaining UI object
public BaseView Resolve(BaseViewPresenter presenter)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
BaseView view = Activator.CreateInstance(viewType) as BaseView;
view.Presenter = presenter;
return view;
}
Вы ожидаете рассматривать тип как ковариантный относительно общего аргумента. Классы никогда не могут быть ковариантными; вам нужно будет использовать интерфейс, а не (или в дополнение к) абстрактный класс, чтобы сделать его ковариантным относительно T
. Вам также нужно будет использовать C # 4.0.