Question

J'ai écrit des pages ASP.NET qui va gérer les formulaires. Ils sont basés sur la classe de base suivante.

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

Et un échantillon est ici SubClass:

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

Ci-dessous j'ai un usercontrol sur la page que je veux « consommer » le « FormData » de la page afin qu'elle puisse lire / écrire.

Je puis, un contrôle plus utilisateur « commun » que je veux avoir fonctionner sur la base d'interface de toutes mes sous-classes de forme ... IMasterForm

Mais quand le usercontrol essaie casting Page.FormData (ayant essayé de jeter page il me dit IHasFormData<IMasterForm> que la page est IHasFormData<IFormSubclass> même si j'ai une contrainte sur la IFormSubclass qui dit qu'il est aussi IMasterForm

Y at-il de toute façon que je peux jetai de la sous-classe générique à la superclasse générique ou est-ce « covariance » et une chose 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

}
Était-ce utile?

La solution

Permettez-moi tout d'abord voir si je peux reformuler ce problème compliqué plus succinctement. Vous disposez d'un IHasFormData<T> d'interface générique. Vous avez un objet qui est connu pour mettre en œuvre IHasFormData<IFormSubclass>. Vous souhaitez convertir en IHasFormData<IMasterForm>. Vous savez qu'il ya une conversion de référence de IFormSubclass à IMasterForm. Cela échoue.

Oui?

Si tel est une déclaration correcte du problème, alors oui, cela est une question de covariance d'interface. C # 3 ne prend pas en charge covariance d'interface. C # 4 sera, si vous pouvez prouver au compilateur que covariance est sûr.

Permettez-moi de vous décrire brièvement pourquoi cela pourrait ne pas être en sécurité. Supposons que vous ayez des classes d'Apple, Orange et fruits avec les relations de sous-classement évidentes. Vous avez un IList<Apple> que vous souhaitez jeter à IList<Fruit>. Cette conversion covariante est pas légal en C # 4 et ne peut pas être légale, car il est pas sûr. Supposons que nous le permettait. Vous pouvez ensuite faire ceci:

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.

Notez que le problème est que List<T> expose une méthode qui prend un T comme argument. Pour que le compilateur pour permettre des conversions covariants sur votre IHasFormData<T> d'interface, vous devez prouver au compilateur que IHasFormData<T> expose rien qui prend un T comme argument. Vous allez le faire en déclarant la IHasFormData<out T> d'interface, un sens mnémonique « T apparaît uniquement dans des positions de sortie ». Le compilateur vérifiera ensuite que votre demande est correcte, et commencer à permettre aux conversions covariants.

Pour plus d'informations sur cette fonction en C # 4, voir mes archives de notes sur la conception de la fonction:

http://blogs.msdn.com/ ericlippert / archive / tags / covariance + et + contravariance / default.aspx

Autres conseils

C # avant 4.0 exige que tous les types génériques moulages pour correspondre au paramètre de type exactement. 4.0 introduit co- et contre-variance, mais le casting que vous essayez d'exécuter est impossible dans les versions antérieures.

J'ai une page de base très similaire à la vôtre est ce que je le mien définir.

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);
        }
}

Je pense que vous devez être quelque chose comme FormData = ((IHasFormData<T>) (IMasterForm )Page)).FormData;

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top