Pergunta

Eu escrevi páginas do ASP.NET que gerenciarão formulários. Eles são baseados na seguinte classe base.

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

E uma amostra de subclasse está aqui:

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

Abaixo, tenho um UserControl na página que eu quero "consumir" o "formData" da página para que possa ler/gravar.

Então, tenho um controle de usuário mais "comum" que quero ter operar na interface base de todas as minhas subclasses de formulário ... iMasterForm

Mas quando o UserControl tenta a página de fundição.FormData (tendo tentado lançar a página para IHasFormData<IMasterForm> me diz que a página é IHasFormData<IFormSubclass> Mesmo que eu tenha uma restrição no iFormSubclass que diz que também é o iMasterForm

Existe alguma maneira de que eu possa lançar da subclasse genérica para a superclasse genérica ou essa "covariância" e uma coisa 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

}
Foi útil?

Solução

Deixe -me primeiro ver se consigo reafirmar esse problema complicado de maneira mais sucinta. Você tem uma interface genérica IHasFormData<T>. Você tem um objeto que é conhecido por implementar IHasFormData<IFormSubclass>. Você deseja convertê -lo para IHasFormData<IMasterForm>. Você sabe que existe uma conversão de referência do IFORMSubClass para o iMasterForm. Isso falha.

Sim?

Se essa é uma declaração correta do problema, então sim, isso é uma questão de covariância da interface. C# 3 não suporta covariância da interface. C# 4 Will, Se você pode provar ao compilador que a covariância é segura.

Deixe -me descrever para você brevemente por que isso pode não estar seguro. Suponha que você tenha aulas de maçã, laranja e frutas com os óbvios relacionamentos de subclasse. Você tem um IList<Apple> que você gostaria de lançar para IList<Fruit>. Essa conversão covariante não é legal no C# 4 e não pode ser legal porque não é segura. Suponha que permitimos isso. Você poderia fazer isso:

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.

Observe que o problema é que List<T> expõe um método que toma um argumento. Para que o compilador permita conversões covariantes em sua interface IHasFormData<T>, você deve provar ao compilador que IHasFormData<T> não expõe nada que tome um T como argumento. Você fará isso declarando a interface IHasFormData<out T>, um significado mnemônico "t aparece apenas em posições de saída". O compilador verificará se sua reivindicação está correta e começará a permitir as conversões covariantes.

Para obter mais informações sobre esse recurso no C# 4, consulte meu arquivo de notas sobre o design do recurso:

http://blogs.msdn.com/ericlippert/archive/tags/covarince+and+contravariance/default.aspx

Outras dicas

C# Antes de 4.0, requer todos os elencos a tipos genéricos para corresponder exatamente ao parâmetro de tipo. 4.0 Introduz co-variação, mas o elenco que você está tentando executar é impossível nas versões anteriores.

Eu tenho uma página base muito semelhante à sua, é assim que defino o meu.

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

Eu acho que você precisa ser algo como FormData = ((IHasFormData<T>) (IMasterForm )Page)).FormData;

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top