这是一个协方差问题?不知道砖墙
-
18-09-2019 - |
题
我写这将管理表单的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;