C# 为什么可以';我是否使用协方差和两个泛型类型参数?

C# 为什么可以';我是否使用协方差和两个泛型类型参数?,c#,generics,covariance,C#,Generics,Covariance,考虑以下示例: class Base {} class Derived : Base {} class Test1 { private List<Derived> m_X; public IEnumerable<Base> GetEnumerable() { return m_X; } } 我得到了编译器错误 无法转换表达式类型 返回类型为“System.Collection.Generic.List” 'Syste

考虑以下示例:

class Base {}

class Derived : Base {}

class Test1
{
    private List<Derived> m_X;

    public IEnumerable<Base> GetEnumerable()
    {
        return m_X;
    }
}
我得到了编译器错误

无法转换表达式类型 返回类型为“System.Collection.Generic.List” 'System.Collection.Generic.IEnumerable'


我做错了什么?

事情是,在第一种情况下,
Base
是一个类。在第二种情况下,类型参数
T
可以是类或结构(这是编译器的想法)

通过指定
T
为类来解决此问题,错误将消失:

class Test2<TBase, TDerived> where TDerived : class, TBase
{
    private List<TDerived> m_X;

    public IEnumerable<TBase> GetEnumerable()
    {
        return m_X;
    }
}
类Test2,其中t派生:类,TBase
{
私有列表m_X;
公共IEnumerable GetEnumerable()
{
返回m_X;
}
}

因此,编译器试图向我们展示TDerived可以是一个结构(因为您没有指定
约束),并且,协方差和逆变不适用于结构

我想一定有一个解释,但是使用
return m_X.Cast()
将解决您的问题。@roryap不,您不能派生结构或从结构中派生。@AgentFire--那么为什么您需要显式,例如
where TDerived:class,TBase
where TDerived:TBase
相比,因为
TBase
是一个类。@roryap因为您可以用结构类型声明类型参数,比如int:
void Foo(),其中T:int
@roryap阅读Eric Gunnerson(C#编译器团队成员)的博客文章“”。这很好地解释了为什么您描述的功能可能没有进入程序。@roryap您所说的是C规则。在CIL中,此规则不适用(所有C#结构都派生自一个类)。当一个项目建成时,它不再是C#,而是CIL。但您仍然可以使用C#项目中的代码(例如,那些泛型类型)。因此,我猜想C#编译器并不认为它自己的规则也被所有可能的CLI语言隐式执行。因此,它需要一个显式约束。
class Test2<TBase, TDerived> where TDerived : class, TBase
{
    private List<TDerived> m_X;

    public IEnumerable<TBase> GetEnumerable()
    {
        return m_X;
    }
}