我有一个带有协变类型参数的接口(interface):

interface I<out T>
{
    T Value { get; }
}

另外,我有一个非泛型基类,并且派生自它:
class Base
{
}

class Derived : Base
{
}

Covariance表示可以将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?)
    }
}

为什么这两种情况不一样?

最佳答案



正确的。



您的陈述过于笼统。您的逻辑似乎是这样的:

  • 协方差表示可以将I<Derived>分配给I<Base>
  • DerivedBase是具有父类(super class)型与子类型关系的任意类型。
  • 因此,协方差适用于任何具有父类(super class)型与子类型关系的类型。
  • 因此,协方差适用于受约束具有此类关系的通用类型参数。

  • 尽管合理,但这种逻辑链是错误的。正确的逻辑链是:
  • 协方差表示可以将I<Derived>分配给I<Base>
  • DerivedBase是具有父类(super class)-子类关系的任意引用类型。
  • 因此,协方差适用于任何具有父类(super class)-子类关系的引用类型。
  • 因此,协方差与通用类型参数一起使用,这些参数被约束为具有此类关系的引用类型。

  • 在您的示例中,制作Foo<int, object>完全在权限之内。由于I<int>无法转换为I<object>,因此编译器拒绝从I<TDerived>转换为I<TBase>的转换。请记住,编译器必须证明通用方法才能对所有可能的构造起作用,而不仅仅是您所做的构造。泛型不是模板。

    我同意,错误消息可能更清晰。我为糟糕的经历表示歉意。改善该错误已列在我的 list 上,但我从未做到。

    您应该在通用类型参数上添加class约束,然后它将按预期工作。

    10-07 19:10
    查看更多