泛型类型参数中的多态性

时间:2017-07-26 09:51:24

标签: c# .net generics polymorphism

我试图在C#中使用泛型类型参数中的多态。我已经审核了其他几个问题(123456),但我和#39 ;我还不清楚为什么这不起作用(或者甚至是否允许)。

背景

我有以下课程:

public class Base { }

public class Derived<T> : Base { }

public class Foo { }

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

var derived = new Derived<Foo>();

基础知识

以下陈述都是正确的:

derived is Object

derived is Base

derived is Derived<Foo>

问题

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

var lazy = new Lazy<Derived<Foo>>();

以下是真实的:

lazy is Lazy<Derived<Foo>>

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

lazy is Lazy<Object>

lazy is Lazy<Base>

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

2 个答案:

答案 0 :(得分:6)

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

public class Generic<out T> {}

然后Generic<Derived> is Generic<Base>

如果您定义Generic类,如:

public class Generic<in T> {}

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

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

public class Generic<T> 
{
    public void Func1(T input);
    public T Func2();
}

想象一下,我们有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();
}

如果我们将Generic<Derived>对象用作Generic<Base>,我们之前在Func2中的解释将会正常工作。出于同样的原因:

public class Generic<in T> 
{
    public void Func1(T input);
}

如果对象Generic<Base>将用作Generic<Derived>,则Func1将正常工作 - 在这种情况下,我们将始终作为参数传递给Func1派生对象,并且根据定义,Dervied始终为Base。

答案 1 :(得分:0)

基本上,有一个通用类型的属性称为不变性。

IList<string> derivedList = new List<string>();    
IList<object> baseList = derivedList;

上面的代码无法编译。

玩继承应用于类而不是类型参数的继承时,多态规则。 C#用协方差和对数放宽了不变性规则。但同样,只是不要将类型参数视为类型本身,它更多是一个参数。