一般约束类中的协方差误差

时间:2018-06-19 18:49:01

标签: c# generics covariance

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

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

此外,我还有一个非泛型基类,并且派生自它:

class Base
{
}

class Derived : Base
{
}

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

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

2 个答案:

答案 0 :(得分:11)

  

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

正确。

  

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

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

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

尽管合理,但逻辑链是错误的。正确的逻辑链是:

  • 协方差表示可以将I<Derived>分配给I<Base>
  • DerivedBase是具有 superclass-subclass 关系的任意引用类型。
  • 因此,协方差适用于具有超类与子类关系的 any reference 类型。
  • 因此,协方差适用于受约束为具有这种关系的引用类型的通用类型参数

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

我同意,错误消息可能更清晰。我为糟糕的经历表示歉意。改善该错误已列在我的清单上,但我从没尝试过。

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

答案 1 :(得分:-2)

通用参数 TDerived TBase 没有对创建的 Derived Base 类的引用。它只是与您在where子句中描述的关系相同的任何类的别名