【问题标题】:.NET 4.0 Cast Generic Interfaces.NET 4.0 Cast 通用接口
【发布时间】:2012-07-16 08:52:30
【问题描述】:

我有两个接口,其中一个是通用接口,只允许从第二个接口派生的类型。它们看起来像这样:

public interface IProvider<T> where T : IContent
{
    T getContent(int i);
    void addContent(T content);
}
public interface IContent
{
    string whatIAm();
}

当然,我的真实接口更复杂,但足以说明我的问题是什么。 现在我为每个接口都有一个具体的类:

public class Provider : IProvider<FileContent> 
{
    public FileContent getContent(int i)
    {
        return null;
    }
    public void addContent(FileContent content)
    {
    }
}

public class FileContent : IContent{
    public string whatIAm(){
        return "FileContent";
    }
}

在我的代码中,我想使用引用类型“IProvider”,但转换出错了......请看这个例子:

 static void Main(string[] args)
    {
        Provider p = new Provider(); //works
        IProvider<FileContent> pp = p as IProvider<FileContent>; //also works
        IProvider<IContent> ppp = pp as IProvider<IContent>; //fails :(
    }

ppp 始终为空。我必须改变这个演员阵容的工作吗? 提前致谢。

【问题讨论】:

标签: .net generics interface casting


【解决方案1】:

类型参数必须完全匹配。 IProvider&lt;IContent&gt;IProvider&lt;FileContent&gt; 是不同的类型,它们之间没有继承关系。

假设您有一个来自您的IProvider&lt;FileContent&gt;IProvider&lt;IContent&gt; ppp,而开发人员尝试使用ppp.addContent(someOtherContentThatIsNoFileContent)。该声明对IProvider&lt;IContent&gt; 有效,但它会破坏类型安全,因此不允许这样的转换是正确的做法。

Covariance and Contravariance for generic type parameters 在某些情况下允许这样的事情,但由于你的接口使用类型参数作为输入和输出参数,这不适用于它现在声明的方式。

编辑:IEnumerable的定义:

public interface IEnumerable<out T> 

所以您知道 IEnumerable 仅将 T 用作输出参数(您不能添加项目,只能枚举它们),而 out 关键字指定 T 是协变的。所以你可以这样做

IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

如果你想这样做,你必须从你的界面中删除add 方法。同样适用于输入参数和泛型类型参数上的 in 关键字。

您的界面将如下所示:

public interface IProvider<out T> where T : IContent
{
    T getContent(int i);
}

【讨论】:

  • 这正是我想要达到的。正如我在问题中已经写的那样:我必须改变这个演员阵容的工作吗?我已经尝试删除 IProvider 接口中的两种方法之一 - 但无论我删除哪一种,强制转换仍然不起作用:(
  • @user1531730 评论太长了,我更新了答案。
  • @user1531730 很高兴听到。 :) 我想你需要把你的界面分成两个界面。
【解决方案2】:

这不是 C# 中泛型的工作方式。 IProvider&lt;FileContent&gt; 的泛型不是IProvider&lt;IContent&gt; 的子类型。

【讨论】:

    【解决方案3】:

    你写的

    Provider p = new Provider(); //works
    IProvider<FileContent> pp = p as IProvider<FileContent>; //also works
    IProvider<IContent> ppp = pp as IProvider<IContent>; //fails :(
    

    让我们假设所有这三个都有效。那么就可以这样写:

    ppp.addContent(new NonFileContent());
    

    其中NonFileContent 是实现IContent 的类,但不是从FileContent 派生的。

    现在,想象一下下面的调用会发生什么:

    FileContent fc = pp.getContent(0);
    

    应该返回刚刚添加的对象。但是,它是 NonFileContent 实例,而不是 FileContent 实例。因此,从返回值必须是 FileContent 实例的方法中返回此对象是不可能的,这就是编译器首先不考虑 ppppp 赋值兼容的原因。

    【讨论】:

    • 好的,我想开始明白为什么它不起作用了。但是参考Botz3000发布的链接,如果我从界面中删除void addContent(T content);方法应该是可能的-但这也不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 2019-03-22
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 2015-02-15
    相关资源
    最近更新 更多