为什么我不能指定一个名单,其中,衍生GT;到List&LT;基地&GT;? [英] Why can't I assign a List<Derived> to a List<Base>?
问题描述
我定义了下面的类:
公共抽象类AbstractPackageCall
{
...
}
我还定义了这个类的子类:
类PackageCall:AbstractPackageCall
{
...
}
也有 AbstractPackageCall
现在我想提出以下电话:
名单,其中,AbstractPackageCall&GT;调用= package.getCalls();
不过,我总是得到此异常:
错误13无法隐式转换类型'System.Collections.Generic.List&LT; Prototype_Concept_2.model.PackageCall&GT;以System.Collections.Generic.List&LT; Prototype_Concept_2.model.AbstractPackageCall&GT;
有什么问题吗?这是该方法包#getCalls
内部列表&LT; PackageCall&GT; getCalls()
{
回电话;
}
要理解为什么这是不允许的,最简单的方法是下面的例子:
抽象类水果
{
}
类苹果:水果
{
}
类香蕉:水果
{
}
//这应该直观地编译吧?原因是苹果是水果。
名单&LT;水果GT;水果=新的名单,其中,苹果&GT;();
//但是,如果我做了什么呢?添加香蕉和苹果的列表
fruits.Add(新香蕉());
最后一条语句会毁了.NET的类型安全。
数组但是,确实让这样的:
水果[]水果=苹果新[10]; //这是完全正常
然而,把一个香蕉
到水果
仍然会打破类型安全,所以为此.NET已经做了键入检查每个阵列插入的,如果它并不是一个真正的苹果
抛出异常。这是一个潜在的(小)的性能损失,但是这可以通过创建围绕任何一种类型的一个结构
包装规避因为这种检查不会发生的值类型(因为他们能够牛逼继承任何东西)。起初,我不明白为什么这个决定是的,但你会遇到很多时候,为什么这可能是有用的。最常见的就是的String.Format
,这需要 params对象[]
和任何数组可以传递到这一点。
在.NET 4中虽然有类型安全的协方差/逆变,它可以让你做一些作业像这些,但只有当他们是可证明安全的。什么是可证明安全的?
的IEnumerable&LT;水果GT;水果=新的名单,其中,苹果&GT;();
在.NET 4以上的作品,因为的IEnumerable&LT; T&GT;
成为的IEnumerable&LT;出T&GT;
。该退出
表示 T
永远只能来的出的
的水果,并且还有的所有的关于的IEnumerable&LT没有方法了T&GT;
有史以来接受牛逼
作为参数,这样你就可以永远正确传递香蕉
的到的的IEnumerable&LT;水果GT ;。
逆变很多相同的,但我总是忘记它的具体细节。不出所料,对于有现在在
关键字的类型参数。
I defined the following class:
public abstract class AbstractPackageCall
{
...
}
I also define a subclass of this class:
class PackageCall : AbstractPackageCall
{
...
}
There are also several other subclases of AbstractPackageCall
Now I want to make the following call:
List<AbstractPackageCall> calls = package.getCalls();
But I always get this exception:
Error 13 Cannot implicitly convert type 'System.Collections.Generic.List<Prototype_Concept_2.model.PackageCall>' to 'System.Collections.Generic.List<Prototype_Concept_2.model.AbstractPackageCall>'
What is the problem here? This is the method Package#getCalls
internal List<PackageCall> getCalls()
{
return calls;
}
The simplest way to understand why this is not allowed is the following example:
abstract class Fruit
{
}
class Apple : Fruit
{
}
class Banana : Fruit
{
}
// This should intuitively compile right? Cause an Apple is Fruit.
List<Fruit> fruits = new List<Apple>();
// But what if I do this? Adding a Banana to a list of Apples
fruits.Add(new Banana());
The last statement would ruin the type safety of .NET.
Arrays however, do allow this:
Fruit[] fruits = new Apple[10]; // This is perfectly fine
However, putting a Banana
into fruits
would still break type safety, so therefor .NET has to do a type check on every array insertion and throw an exception if it's not actually an Apple
. This is potentially a (small) performance hit, but this can be circumvented by creating a struct
wrapper around either type as this check does not happen for value types (because they can't inherit from anything). At first, I didn't understand why this decision was made, but you'll encounter quite often why this can be useful. Most common is String.Format
, which takes params object[]
and any array can be passed into this.
In .NET 4 though, there's type safe covariance/contravariance, which allows you to make some assignments like these, but only if they're provably safe. What's provably safe?
IEnumerable<Fruit> fruits = new List<Apple>();
The above works in .NET 4, because IEnumerable<T>
became IEnumerable<out T>
. The out
means that T
can only ever come out of fruits
and that there's no method at all on IEnumerable<out T>
that ever takes T
as a parameter, so you can never incorrectly pass a Banana
into IEnumerable<Fruit>
.
Contravariance is much the same but I always forget the exact details on it. Unsurprisingly, for that there's now the in
keyword on type parameters.
这篇关于为什么我不能指定一个名单,其中,衍生GT;到List&LT;基地&GT;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!