c# - 这是协方差问题吗?不确定是否砖墙

标签 c# .net generics .net-3.5 covariance

我编写了管理表单的 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”,以便它可以读/写它。

然后,我有一个更“通用”的用户控件,我想在我所有表单子(monad)类的基本接口(interface)上操作... IMasterForm

但是当用户控件尝试转换 Page.FormData(试图将页面转换到 IHasFormData<IMasterForm> 它告诉我页面是 IHasFormData<IFormSubclass> 即使我对 IFormSubclass 有一个约束说它也是 IMasterForm

我是否可以从通用子类转换为通用父类(super class),或者这是“协方差”和 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

}

最佳答案

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

是吗?

如果这是对问题的正确陈述,那么是的,这是一个接口(interface)协变的问题。 C# 3 不支持接口(interface)协变。 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.

注意问题是 List<T>公开一个以 T 作为参数的方法。为了使编译器允许在您的接口(interface)上进行协变转换 IHasFormData<T> , 你必须向编译器证明 IHasFormData<T>不公开任何以 T 作为参数的内容。您将通过声明接口(interface) IHasFormData<out T> 来做到这一点, 一个助记符,意思是“T 只出现在输出位置”。然后编译器将验证您的声明是否正确,并开始允许协变转换。

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

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

关于c# - 这是协方差问题吗?不确定是否砖墙,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2008054/

相关文章:

c# - 如何使 GetFiles() 排除扩展名以搜索扩展名开头的文件?

c# - Windows Phone 7 中的列表框项目

c# - 使用 C# 在 postgresql 中返回最后插入的 ID

c# - 为什么我的 ASP.NET WebBrowser 控件无法正确呈现自定义图标字体?

c# - 这是 DIP (SOLID) 的有效使用吗?

.net - 将解决方案更新到.net Framework 4.7 : issue with Roslyn

.net - 希望 .NET 泛型可以继承其中一种泛型参数类型的充分理由是什么?

c# - 如何最好地构建此 API 以存储通用委托(delegate)以供以后执行

java - 使用泛型返回参数类的实例

generics - 如何使用 Guava Optional 作为 "naturally covariant object"