Кастинг в общий интерфейс [дубликат]

Используйте thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
10
задан Rachel 11 September 2014 в 17:31
поделиться

3 ответа

Это очень часто задаваемый вопрос. Давайте переименуем ваши типы:

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, демонстрирующая этот факт:

enter image description here [/g6]

Вы хотите, чтобы функция общая контравариантность , и поддерживается только на интерфейсах интерфейсов и делегатов , когда компилятор может доказать, что дисперсия безопасна, а когда переменный тип является ссылкой тип. Например, вы можете использовать IEnumerable<Apple> в контексте, где требуется IEnumerable<Fruit>, потому что компилятор может проверить, что вы не можете поместить Banana в последовательность фруктов.

Проведите поиск по «ковариации и ковариации C #» на этом сайте или в Интернете, и вы найдете гораздо больше информации о том, как работает эта функция. В частности, моя серия статей о том, как мы разработали и реализовали эту функцию на C # 4, начинается здесь: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and- контрвариантность-в-с-часть-one.aspx

38
ответ дан Eric Lippert 17 August 2018 в 18:37
поделиться
  • 1
    @ Рейчел: Добро пожаловать! Джон Скит обычно использует чаши, фрукты, яблоки и бананы при ответе на этот вопрос; Обычно я использую клетки, животных, жирафов и тигров, что более интересно. Паддок, полный жирафов, нельзя использовать как клетку для животных, потому что вы можете положить туда тигра, который будет есть жирафов. Что вызывает вопрос: действительно ли тигр попытается съесть жирафа? Жирафы сильно напрягаются. – Eric Lippert 11 September 2014 в 17:46
  • 2
    С другой стороны, Джон действительно может сделать демонстрацию с фруктами (см. Фото), и я не могу легко сделать это с жирафами. – Eric Lippert 11 September 2014 в 17:53
  • 3
    @Rachel или, может быть, есть некоторое подмножество контракта baseview, что (1) - это то, что вам действительно нужно от возвращаемого объекта, и (2) не обязательно должно быть общим, в то время как для некоторой детали реализации требуется быть общим. Затем вы можете вывести эти члены в базовый класс или интерфейс и использовать этот тип в качестве возвращаемого типа Resolve. Более общий тип типа может иметь ссылку на конкретный тип представления, который он представляет, не обязательно подвергая его потребителю метода. – phoog 11 September 2014 в 18:05
  • 4
    @Rachel в этом случае вы могли бы придумать что-то умное, но, вероятно, лучше спросить в отдельном вопросе. Но если бы я не искал упражнение в дженериках (которое я часто бываю), я бы очень хотел просто указать DataContext в установщике свойств или методе набора - вы уже лишили объект, возвращенный из Activator.CreateInstance. Кастеты довольно дешевые - на самом деле это всего лишь проверка типа. – phoog 11 September 2014 в 22:01
  • 5
    Я только что нашел это сегодня, и это мой новый любимый ответ на SO. – BJ Myers 3 May 2017 в 16:44

Я принял ответ Эрика, так как он дает отличное объяснение, почему то, что я хотел, было невозможно, но я также подумал, что поделюсь своим решением, если кто-то еще столкнется с этой проблемой.

Я удалил параметр 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;
}
8
ответ дан Andrew Hanlon 17 August 2018 в 18:37
поделиться
  • 1
    Вы можете использовать метод расширения, чтобы получить желаемую функциональность из исходного вопроса. Просто создайте метод расширения как: public static BaseView & lt; T & gt; Разрешить & lt; T & gt; (этот T-презентатор), где T: BaseViewPresenter И затем преобразовать в BaseView & lt; T & gt; вместо BaseView. – Andrew Hanlon 17 September 2014 в 19:01
  • 2
    @AndrewHanlon Это не сработает, потому что я не хочу указывать конкретный тип для использования метода .Resolve. Вот почему мой вопрос указывает «без использования специальных типов». Однако, спасибо :) – Rachel 17 September 2014 в 19:10
  • 3
    Если вы использовали его так же, как в своем вопросе (var login = new LoginPresenter; var ctrl = LoginPresenter.Resolve ();), то он действительно будет работать правильно и вернуть BaseView & lt; LoginPresenter & gt; - не требуется общая спецификация. – Andrew Hanlon 17 September 2014 в 21:34
  • 4
    @AndrewHanlon Да, я сожалею об этом образце кода, я использовал его для простоты. На самом деле мой метод .Resolve() вызывается из кода позади пользовательского UserControl, и передатчик, переданный в него, является динамическим и неизвестным с помощью UserControl в момент его вычисления. Все, о чем он заботится, это тип BaseViewPresenter – Rachel 17 September 2014 в 22:13
  • 5
    отличная благодарность за хороший ответ – Arunprasanth K V 8 September 2016 в 13:45

Вы ожидаете рассматривать тип как ковариантный относительно общего аргумента. Классы никогда не могут быть ковариантными; вам нужно будет использовать интерфейс, а не (или в дополнение к) абстрактный класс, чтобы сделать его ковариантным относительно T. Вам также нужно будет использовать C # 4.0.

3
ответ дан Servy 17 August 2018 в 18:37
поделиться
  • 1
    LoginView наследует от BaseView<LoginPresenter>, а LoginPresenter наследует от BaseViewPresenter, поэтому в теории я бы предположил, что его можно отличить LoginView до BaseView<BaseViewPresenter>. Вы говорите, что это невозможно с 3.5? – Rachel 11 September 2014 в 17:33
  • 2
    @Rachel И это предположение было бы неверным. Тип должен быть ковариантным относительно его общего аргумента, чтобы это было действительным. Классы в C # никогда не ковариантны. Интерфейсы имеют потенциал , ковариантный. Синтаксис для поддержки ковариации для интерфейсов был добавлен в C # 4.0. – Servy 11 September 2014 в 17:38
  • 3
    +1. @Rachel - ваше ожидание просто неожиданно. Соотношение между классами не подразумевает никакой связи между классами, использующими их в качестве аргументов generic. То есть List<Base> и List<Derived> - братья и сестры (имеют общий базовый интерфейс), но никак не связаны друг с другом. В этой теме много сообщений - поиск & quot; C # covariance & quot; (можно добавить «Eric Lippert» для получения лучших результатов). – Alexei Levenkov 11 September 2014 в 17:39
Другие вопросы по тегам:

Похожие вопросы: