È questo un problema di covarianza? Non sono sicuro se il muro di mattoni
-
18-09-2019 - |
Domanda
ho scritto le pagine ASP.NET che gestirà le forme. Stanno sulla base della seguente classe di base.
public abstract class FormPageBase<TInterface, TModel> : Page, IKeywordProvider
where TModel:ActiveRecordBase<MasterForm>, TInterface, new()
where TInterface:IMasterForm
{
public TInterface FormData { get; set; }
}
E una sottoclasse campione è qui:
public partial class PersonalDataFormPage : FormPageBase<IPersonalDataForm, PersonalDataForm>, IHasFormData<IPersonalDataForm>, IHasContact
{
}
Qui di seguito ho un UserControl sulla pagina di cui voglio "consumare" il "formdata" dalla pagina in modo che possa leggere / scrivere ad esso.
Ho poi, avere un maggiore controllo utente "comune" che voglio avere operare sul interfaccia di base di tutti i miei sottoclassi di forma ... IMasterForm
Ma quando l'UserControl cerca colata Page.FormData (aver tentato di gettare pagina ad IHasFormData<IMasterForm>
mi dice che la pagina è IHasFormData<IFormSubclass>
anche se ho un vincolo sulla IFormSubclass che dice che è anche IMasterForm
È comunque ci che posso caccio dalla sottoclasse generica alla superclasse generici o è questo "covarianza" e una cosa 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
}
Soluzione
Permettetemi innanzitutto di vedere se riesco a riformulare questo problema complicato più brevemente. Si dispone di un IHasFormData<T>
interfaccia generica. Si dispone di un oggetto che è noto per implementare IHasFormData<IFormSubclass>
. Si desidera convertire a IHasFormData<IMasterForm>
. Voi sapete che c'è una conversione di riferimento da IFormSubclass a IMasterForm. Ciò non è riuscito.
Sì?
Se questo è una corretta dichiarazione del problema, allora sì, questa è una questione di interfaccia di covarianza. C # 3 non supporta l'interfaccia di covarianza. C # 4 sarà, se si può dimostrare al compilatore che covarianza è sicuro.
Vorrei descrivere per voi brevemente perché questo potrebbe non essere al sicuro. Supponiamo di avere classi di mela, arancia e frutta con le relazioni sottoclasse evidenti. Hai IList<Apple>
che si desidera lanciare a IList<Fruit>
. Che la conversione covariante non è legale in C # 4 e non può essere legale perché non è sicuro. Supponiamo di permesso. Si potrebbe quindi fare questo:
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.
Si noti che il problema è che List<T>
espone un metodo che accetta un T come argomento. Affinché il compilatore per consentire conversioni covarianti sul IHasFormData<T>
interfaccia, è necessario dimostrare al compilatore che IHasFormData<T>
espone nulla che prende una T come argomento. Farete che dichiarando la IHasFormData<out T>
di interfaccia, un significato mnemonico "T appare solo nelle posizioni di uscita". Il compilatore quindi verificare che il vostro reclamo è corretto, e iniziare permettendo alle conversioni covarianti.
Per ulteriori informazioni su questa funzione in C # 4, vedere il mio archivio di note sulla progettazione della funzione:
http://blogs.msdn.com/ ericlippert / archivio / tag / covarianza + e + controvarianza / default.aspx
Altri suggerimenti
C # prima del 4.0 richiede tutti i calchi di tipi generici in base al parametro di tipo esattamente. 4.0 introduce co- e contro-varianza, ma il cast si sta tentando di eseguire è impossibile nelle versioni precedenti.
Ho una pagina di base molto simili ai tuoi questo è come mi definisco il mio.
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);
}
}
Credo che tu debba essere qualcosa di simile FormData = ((IHasFormData<T>) (IMasterForm
)Page)).FormData;