为什么我不能指定一个名单,其中,衍生GT;到List<基地>? [英] Why can't I assign a List<Derived> to a List<Base>?

查看:132
本文介绍了为什么我不能指定一个名单,其中,衍生GT;到List<基地>?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我定义了下面的类:

 公共抽象类AbstractPackageCall
    {

     ...

    }
 

我还定义了这个类的子类:

 类PackageCall:AbstractPackageCall
    {

      ...
    }
 

也有 AbstractPackageCall

若干其他subclases

现在我想提出以下电话:

 名单,其中,AbstractPackageCall>调用= package.getCalls();
 

不过,我总是得到此异常:

 错误13无法隐式转换类型'System.Collections.Generic.List< Prototype_Concept_2.model.PackageCall>以System.Collections.Generic.List< Prototype_Concept_2.model.AbstractPackageCall>
 

有什么问题吗?这是该方法包#getCalls

 内部列表< PackageCall> getCalls()
        {
            回电话;
        }
 

解决方案

要理解为什么这是不允许的,最简单的方法是下面的例子:

 抽象类水果
{
}

类苹果:水果
{
}

类香蕉:水果
{
}

//这应该直观地编译吧?原因是苹果是水果。
名单<水果GT;水果=新的名单,其中,苹果>();

//但是,如果我做了什么呢?添加香蕉和苹果的列表
fruits.Add(新香蕉());
 

最后一条语句会毁了.NET的类型安全。

数组但是,确实让这样的:

 水果[]水果=苹果新[10]; //这是完全正常
 

然而,把一个香蕉水果仍然会打破类型安全,所以为此.NET已经做了键入检查每个阵列插入的,如果它并不是一个真正的苹果抛出异常。这是一个潜在的(小)的性能损失,但是这可以通过创建围绕任何一种类型的一个结构包装规避因为这种检查不会发生的值类型(因为他们能够牛逼继承任何东西)。起初,我不明白为什么这个决定是的,但你会遇到很多时候,为什么这可能是有用的。最常见的就是的String.Format ,这需要 params对象[] 和任何数组可以传递到这一点。

在.NET 4中虽然有类型安全的协方差/逆变,它可以让你做一些作业像这些,但只有当他们是可证明安全的。什么是可证明安全的?

 的IEnumerable<水果GT;水果=新的名单,其中,苹果>();
 

在.NET 4以上的作品,因为的IEnumerable< T> 成为的IEnumerable<出T> 。该退出表示 T 永远只能来的的水果,并且还有的所有的关于的IEnumerable&LT没有方法了T> 有史以来接受牛逼作为参数,这样你就可以永远正确传递香蕉的IEnumerable<水果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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆