4

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

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始终为空。我必须改变这个演员阵容的工作吗?提前致谢。

4

3 回答 3

6

类型参数必须完全匹配。IProvider<IContent>是不同的类型IProvider<FileContent>,它们之间没有继承。

想象一下,您有一个IProvider<IContent> ppp  来自您IProvider<FileContent>和一个开发人员的尝试ppp.addContent(someOtherContentThatIsNoFileContent)。该声明对 有效IProvider<IContent>,但它会破坏类型安全,因此不允许这样的转换是正确的做法。 

泛型类型参数的协变和逆变在某些情况下允许这样的事情,但由于您的接口使用类型参数作为输入参数和输出参数,这不适用于它现在声明的方式。

编辑:看看IEnumerable' 的定义:

public interface IEnumerable<out T> 

因此,您知道 IEnumerableT仅用作输出参数(您不能添加项目,只能枚举它们),并且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);
}
于 2012-07-17T12:39:57.750 回答
3

这不是 C# 中泛型工作的方式。泛型 ofIProvider<FileContent>不是 的子类型IProvider<IContent>

于 2012-07-17T12:34:27.473 回答
1

你写了

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());

whereNonFileContent是一个实现IContent,但不是派生自的类FileContent

现在,想象一下在以下调用中会发生什么:

FileContent fc = pp.getContent(0);

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

于 2012-07-17T12:46:08.280 回答