C#泛型,接口和继承

Ale*_*lex 6 c# generics

我有两个接口:

public interface IAmA
{
}

public interface IAmB<T> where T : IAmA
{
}
Run Code Online (Sandbox Code Playgroud)

实现这些接口的两个类如下:

public class ClassA : IAmA
{
}

public class ClassB : IAmB<ClassA>
{
}
Run Code Online (Sandbox Code Playgroud)

尝试使用这些类时如下所示:

public class Foo
{
    public void Bar()
    {
        var list = new List<IAmB<IAmA>>();
        list.Add(new ClassB());
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到这个编译器错误:

cannot convert from 'ClassB' to 'IAmB<IAmA>'

我知道我可以使编译器使用:

public class ClassB : IAmB<IAmA>
{
}
Run Code Online (Sandbox Code Playgroud)

不过,我需要能够成为该类型参数IAmB<>ClassB的一个实现IAmA.

Pan*_*vos 11

快速回答是,只有当类型用作返回类型时,您才可以通过将类型参数声明IAmB<T>为协变执行您所要求的操作:

public interface IAmB<out T> where T : IAmA
{
    T SomeMethod(string someparam);
}
Run Code Online (Sandbox Code Playgroud)

out T 意味着您可以使用比约束中指定的更具体的类型.

您将无法使用T作为参数.以下内容无法编译:

public interface IAmB<out T> where T : IAmA
{
    void SomeMethod(T someparam);
}
Run Code Online (Sandbox Code Playgroud)

从文档中

您可以使用协变类型参数作为属于接口的方法的返回值,或者作为委托的返回类型.您不能将协变类型参数用作接口方法的泛型类型约束.

这不是编译器的怪癖. 假设你可以声明一个协变方法参数,你的列表最终将包含一些无法处理IAmB<IAmA>参数的对象- 他们希望输入ClassA或更具体.您的代码将编译但在运行时失败.

这引出了一个问题 - 为什么要使用IAmB<ClassA>

您应该在使用之前考虑一下,因为可能有其他更合适的方法来解决您的实际问题.使用实现具体类型的通用接口但尝试使用它就好像它正在实现另一个接口一样是不寻常的.

您可以查看MSDN文档中关于协方差和逆变的部分以及Eric Lippert的Jon Skeet 对此SO问题的回答:协方差和逆变差之间的差异