这是一个协方差问题吗?不确定是否砖墙

Fam*_*erd 4 .net c# generics covariance .net-3.5

我编写了ASP.NET页面来管理表单.它们基于以下基类.

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

一个示例SubClass在这里:

public partial class PersonalDataFormPage : FormPageBase<IPersonalDataForm, PersonalDataForm>, IHasFormData<IPersonalDataForm>, IHasContact
    {
    }
Run Code Online (Sandbox Code Playgroud)

下面我在页面上有一个usercontrol,我想从页面"使用""FormData",以便它可以读/写它.

然后,我有一个更"通用"的用户控件,我想在我所有的表单子类的基接口上操作... IMasterForm

但是当usercontrol尝试强制转换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

}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 14

让我先看看能否更简洁地重述这个复杂的问题.你有一个通用的界面IHasFormData<T>.你有一个已知的实现对象IHasFormData<IFormSubclass>.你希望将其转换为IHasFormData<IMasterForm>.您知道有一个从IFormSubclass到IMasterForm的引用转换.这失败了.

是?

如果这是对问题的正确陈述,那么是的,这是界面协方差的问题.C#3不支持接口协方差.如果你能向编译器证明协方差是安全的,那么 C#4将会出现.

让我简要介绍一下为什么这可能不安全.假设您有Apple,Orange和Fruit类,具有明显的子类关系.你有一个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.
Run Code Online (Sandbox Code Playgroud)

请注意,问题是List<T>暴露了一个以T为参数的方法.为了让编译器允许在您的接口上进行协变转换IHasFormData<T>,您必须向编译器证明IHasFormData<T>没有公开任何以T为参数的内容.你会通过声明界面来做到这一点,这是IHasFormData<out T>一个助记符,意思是"T只出现在输出位置".然后,编译器将验证您的声明是否正确,并开始允许协变转换.

有关C#4中此功能的更多信息,请参阅我的功能设计说明存档:

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx