我试图在C#中使用泛型类型参数中的多态。我已经审核了其他几个问题(1,2,3,4,5,6),但我和#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>
这是为什么?它们应该是真的还是我误解了仿制药是如何工作的?
答案 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#用协方差和对数放宽了不变性规则。但同样,只是不要将类型参数视为类型本身,它更多是一个参数。