一般约束类中的协方差

Ale*_*x Q 4 c# generics covariance

我有一个协变类型参数的接口:

interface I<out T>
{
    T Value { get; }
}
Run Code Online (Sandbox Code Playgroud)

另外,我有一个非泛型基类,另一个派生自它:

class Base
{
}

class Derived : Base
{
}
Run Code Online (Sandbox Code Playgroud)

协方差说I<Derived>可以分配给一个I<Base>,并且确实I<Base> ib = default(I<Derived>);编译得很好.

但是,这种行为显然会随着继承约束的泛型参数而改变:

class Foo<TDerived, TBase>
    where TDerived : TBase
{
    void Bar()
    {
        I<Base> ib = default(I<Derived>); // Compiles fine
        I<TBase> itb = default(I<TDerived>); // Compiler error: Cannot implicitly convert type 'I<TDerived>' to 'I<TBase>'. An explicit conversion exists (are you missing a cast?)
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么这两个案件不一样?

Eri*_*ert 11

协方差表示I<Derived>可以分配给一个I<Base>

正确.

为什么这两个案件不一样?

你的陈述过于笼统.你的逻辑似乎是这样的:

  • 协方差表示I<Derived>可以分配给一个I<Base>
  • Derived并且Base是具有超类型 - 子类型关系的任意类型.
  • 因此,协方差适用于具有超类型 - 子类型关系的任何类型.
  • 因此,协方差适用于受限于具有这种关系的泛型类型参数.

虽然看似合理,但这种逻辑链是错误的.正确的逻辑链是:

  • 协方差表示I<Derived>可以分配给一个I<Base>
  • Derived并且Base是具有超类 - 子类关系的任意引用类型.
  • 因此,协方差适用于具有超类 - 子类关系的任何 引用类型.
  • 因此,协方差适用于通用类型参数,这些参数被约束为具有这种关系的引用类型.

在你的例子中,一个完全有权制作一个Foo<int, object>.由于I<int>无法转换为I<object>,编译器拒绝转换I<TDerived>I<TBase>.请记住,编译器必须证明泛型方法适用于所有可能的构造,而不仅仅是您构造的构造.泛型不是模板.

我同意,错误信息可能更清楚.我为糟糕的经历道歉.改进这个错误在我的名单上,我从来没有这样做过.

您应该class对通用类型参数设置约束,然后它将按预期工作.

  • +1我不知道方差仅适用于引用类型或原因.我也没有意识到`class`约束实际上限制了引用类型(包括接口,因此通过装箱间接支持结构). (2认同)