泛型类型参数的多态性

Kev*_*zyk 8 .net c# generics polymorphism

我试图在C#中的泛型类型参数中使用多态.我已经在SO(审查其他几个问题1,2,3,4,5,6),但我仍然不清楚为什么这不工作(或者如果它甚至允许的).

背景

我有以下课程:

public class Base { }

public class Derived<T> : Base { }

public class Foo { }
Run Code Online (Sandbox Code Playgroud)

和我的派生泛型类型的实例:

var derived = new Derived<Foo>();
Run Code Online (Sandbox Code Playgroud)

基础

以下陈述都是正确的:

derived is Object

derived is Base

derived is Derived<Foo>
Run Code Online (Sandbox Code Playgroud)

问题

当我尝试将我的派生类用作另一个泛型类型中的类型参数时,我得到一些意外的行为.鉴于以下惰性实例:

var lazy = new Lazy<Derived<Foo>>();
Run Code Online (Sandbox Code Playgroud)

以下是真实的:

lazy is Lazy<Derived<Foo>>
Run Code Online (Sandbox Code Playgroud)

但是当我预期它们是真的时,这些都是错误的:

lazy is Lazy<Object>

lazy is Lazy<Base>
Run Code Online (Sandbox Code Playgroud)

为什么是这样?它们应该是真的还是我误解了仿制药是如何工作的?

mr1*_*100 7

是的,你误解了通用的工作方式.这也是使用Generic类型的最大限制(实际上你应该尽可能地避免使用它们).如果来源于基地继承则是通常不正确的Generic<Derived>Generic<Base>.例外是协方差和逆变.如果你定义你的Generic类,如:

public class Generic<out T> {}
Run Code Online (Sandbox Code Playgroud)

然后 Generic<Derived> is Generic<Base>

如果你定义你的Generic类,如:

public class Generic<in T> {}
Run Code Online (Sandbox Code Playgroud)

然后Generic<Base> is Generic<Derived>(惊讶,嗯?).

为什么简单演员不起作用?想象一下,我们的Generic类看起来如下:

public class Generic<T> 
{
    public void Func1(T input);
    public T Func2();
}
Run Code Online (Sandbox Code Playgroud)

想象一下,我们有Generic<Derived>对象,我们正在使用它Generic<Base>.在这种情况下,Func2完美地工作 - 它返回Derived对象,该对象可以是施法者到Base.但是Func1不起作用 - 我们有一个接受Base对象的函数,但实际对象的Func1只接受Derived对象而不是所有Base对象都是Derived,对吧?

这个例子解释了为什么in和out继承有效.如果我们在泛型类中对类型参数应用约束,我们提交T类型只能从属性或函数返回,但它可能永远不会被接受为参数.在这种情况下,我们的Generic类看起来像这样:

public class Generic<out T> 
{
    public T Func2();
}
Run Code Online (Sandbox Code Playgroud)

正如我们在之前使用过的Func2,如果我们将使用Generic<Derived>对象,它将正常工作Generic<Base>.出于同样的原因:

public class Generic<in T> 
{
    public void Func1(T input);
}
Run Code Online (Sandbox Code Playgroud)

FUNC1将正常工作,如果对象Generic<Base>将被用作Generic<Derived>-在这种情况下,我们会一直传递到FUNC1派生对象作为参数,并Dervied始终是基本定义.

  • @mr100 我在带有类的 LinqPad 中尝试过这个,但是遇到了编译错误。我发现它只适用于接口。 (2认同)