【问题标题】:Why can't I use covariance with two generic type parameters?为什么我不能对两个泛型类型参数使用协方差?
【发布时间】:2015-02-11 23:02:28
【问题描述】:

考虑以下示例:

class Base {}

class Derived : Base {}

class Test1
{
    private List<Derived> m_X;

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

这编译得很好,因为IEnumerable&lt;T&gt;T 中是协变

但是,如果我做同样的事情,但现在使用泛型:

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

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

我得到编译器错误

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

我在这里做错了什么?

【问题讨论】:

  • 我想必须有一个解释,但是,使用return m_X.Cast&lt;TBase&gt;(); 会解决你的情况。

标签: c# generics covariance


【解决方案1】:

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

解决情况,指定T是一个类,错误就会消失:

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

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

因此,编译器试图向我们展示 TDerived 可以是结构(因为您没有指定 class 约束)和 as we already know协变和逆变不适用于结构强>。

【讨论】:

  • @roryap 不,您不能派生结构,也不能从结构派生。
  • @AgentFire -- 那么为什么你需要明确,例如where TDerived : class, TBase vs. where TDerived : TBase 因为TBase 是一个类。?
  • @roryap 因为您可以声明具有结构类型的类型参数,例如 int:void Foo&lt;T&gt;() where T : int
  • @roryap 阅读 Eric Gunnerson(C# 编译器团队的成员)的博文“Minus 100 points”。它很好地解释了为什么像您描述的功能可能没有进入程序。
  • @roryap 你在说的是 C# 规则。在 CIL 中,此规则不适用(所有 C# 结构都派生自一个类)。构建项目时,它不再是 C#,而是 CIL。但是您仍然可以从 C# 项目中使用该代码(例如,那些泛型类型)。因此,我猜 C# 编译器根本不会假设它自己的规则也被所有可能的 CLI 语言隐式强制执行。因此,相反,它需要一个明确的约束。
猜你喜欢
  • 2012-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
相关资源
最近更新 更多