我写这将管理表单的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不支持接口协方差。 C#4将,如果你能证明编译器的协方差是安全的。

让我简述一下你为什么这可能不是安全的。假设你有类苹果,橘子和水果具有明显的子类关系。你有,你想投给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 /存档/标签/协方差+和+逆变/ 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