Используйте thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Это очень часто задаваемый вопрос. Давайте переименуем ваши типы:
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
Теперь ваш вопрос:
У меня есть
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;
}
.Resolve
. Вот почему мой вопрос указывает «без использования специальных типов». Однако, спасибо :)
– Rachel
17 September 2014 в 19:10
.Resolve()
вызывается из кода позади пользовательского UserControl
, и передатчик, переданный в него, является динамическим и неизвестным с помощью UserControl в момент его вычисления. Все, о чем он заботится, это тип BaseViewPresenter
– Rachel
17 September 2014 в 22:13
Вы ожидаете рассматривать тип как ковариантный относительно общего аргумента. Классы никогда не могут быть ковариантными; вам нужно будет использовать интерфейс, а не (или в дополнение к) абстрактный класс, чтобы сделать его ковариантным относительно T
. Вам также нужно будет использовать C # 4.0.
LoginView
наследует от BaseView<LoginPresenter>
, а LoginPresenter
наследует от BaseViewPresenter
, поэтому в теории я бы предположил, что его можно отличить LoginView
до BaseView<BaseViewPresenter>
. Вы говорите, что это невозможно с 3.5?
– Rachel
11 September 2014 в 17:33
List<Base>
и List<Derived>
- братья и сестры (имеют общий базовый интерфейс), но никак не связаны друг с другом. В этой теме много сообщений - поиск & quot; C # covariance & quot; (можно добавить «Eric Lippert» для получения лучших результатов).
– Alexei Levenkov
11 September 2014 в 17:39