Это проблема ковариации?Не уверен, что кирпичная стена

StackOverflow https://stackoverflow.com/questions/2008054

Вопрос

Я написал страницы ASP.NET, которые будут управлять формами.Они основаны на следующем базовом классе.

public abstract class FormPageBase<TInterface, TModel> : Page, IKeywordProvider 
        where TModel:ActiveRecordBase<MasterForm>, TInterface, new()
        where TInterface:IMasterForm
    {
        public TInterface FormData { get; set; }                   
     }

И пример подкласса здесь:

public partial class PersonalDataFormPage : FormPageBase<IPersonalDataForm, PersonalDataForm>, IHasFormData<IPersonalDataForm>, IHasContact
    {
    }

Ниже у меня есть пользовательский элемент управления на странице, который я хочу «потребить» «FormData» со страницы, чтобы он мог читать/записывать в него.

Затем у меня есть более «общий» пользовательский элемент управления, который я хочу использовать в базовом интерфейсе всех моих подклассов формы...IMasterForm

Но когда пользовательский элемент управления пытается привести Page.FormData (попытавшись привести страницу к IHasFormData<IMasterForm> он говорит мне, что страница IHasFormData<IFormSubclass> хотя у меня есть ограничение на IFormSubclass, которое говорит, что это также IMasterForm

Могу ли я в любом случае преобразовать общий подкласс в общий суперкласс или это «ковариация» и вещь C # 4.0?

public abstract class FormControlBase<T> : UserControl, IKeywordProvider
    where T:IMasterForm 
{

    protected T FormData { get; set; }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

//This cast is failing when my common control's T does not exactly match
// the T of the Page.. even though the common controls TInterface is a base interface to the
//pages TInterface

        FormData = ((IHasFormData<T>) Page).FormData;

        if (!IsPostBack)
        {
            PopulateBaseListData();
            BindDataToControls();
        }
    }

    protected abstract void PopulateBaseListData();
    protected abstract void BindDataToControls();


    public abstract void SaveControlsToData();


    #region IKeywordProvider
    public List<IKeyword> GetKeywords(string categoryName)
    {
        if(!(Page is IKeywordProvider ))
            throw new InvalidOperationException("Page is not  IKeywordProvider");

        return ((IKeywordProvider) Page).GetKeywords(categoryName);
    }

    #endregion

}
Это было полезно?

Решение

Позвольте мне сначала посмотреть, смогу ли я еще раз сформулировать эту сложную проблему более кратко.У вас есть общий интерфейс IHasFormData<T>.У вас есть объект, который, как известно, реализует IHasFormData<IFormSubclass>.Вы хотите преобразовать его в IHasFormData<IMasterForm>.Вы знаете, что существует преобразование ссылок из IFormSubclass в IMasterForm.Это терпит неудачу.

Да?

Если это правильная постановка задачи, то да, это вопрос ковариантности интерфейса.C# 3 не поддерживает ковариацию интерфейса.С# 4 будет, если вы сможете доказать компилятору, что ковариация безопасна.

Позвольте мне кратко описать вам, почему это может быть небезопасно.Предположим, у вас есть классы Apple, Orange и Fruit с очевидными отношениями подклассов.У вас есть IList<Apple> к которому вы хотите применить IList<Fruit>.Такое ковариантное преобразование недопустимо в C# 4 и не может быть допустимым, поскольку оно небезопасно.Предположим, мы это разрешили.Затем вы могли бы сделать это:

IList<Apple> apples = new List<Apple>();
IList<Fruit> fruits = apples;
fruits.Add(new Orange()); 
// We just put an orange into a list of apples!  
// And now the runtime crashes.

Обратите внимание, что проблема в том, что List<T> предоставляет метод, который принимает T в качестве аргумента.Чтобы компилятор разрешил ковариантные преобразования в вашем интерфейсе IHasFormData<T>, вы должны доказать компилятору, что IHasFormData<T> не предоставляет ничего, что принимает T в качестве аргумента.Вы сделаете это, объявив интерфейс IHasFormData<out T>, мнемоническое значение «T появляется только в выходных позициях».Затем компилятор проверит правильность вашего утверждения и начнет разрешать ковариантные преобразования.

Дополнительную информацию об этой функции в C# 4 см. в моем архиве заметок о дизайне этой функции:

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

Другие советы

C# до версии 4.0 требует, чтобы все приведения к универсальным типам точно соответствовали параметру типа.В версии 4.0 представлены ко- и контрвариантность, но приведение, которое вы пытаетесь выполнить, невозможно в более ранних версиях.

У меня очень похожая базовая страница на вашу, вот как я определяю свою.

public abstract class ViewBasePage<TPresenter, TView> : Page, IView
        where TPresenter : Presenter<TView>
        where TView : IView
{
    protected TPresenter _presenter;

    public TPresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = (TView) ((IView) this);
        }
}

Я думаю, тебе нужно быть чем-то вроде FormData = ((IHasFormData<T>) (IMasterForm )Page)).FormData;

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top