一尘不染

为什么我无法分配列表 到清单?

c#

我定义了以下类:

public abstract class AbstractPackageCall
    {

     ...

    }

我还定义了此类的子类:

class PackageCall : AbstractPackageCall
    {

      ...
    }

还有其他几个子条款 AbstractPackageCall

现在我要打以下电话:

 List<AbstractPackageCall> calls = package.getCalls();

但是我总是会遇到这样的异常:

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>'

这里有什么问题?这是Package#getCalls方法

 internal List<PackageCall> getCalls()
        {
            return calls;
        }

阅读 231

收藏
2020-05-19

共1个答案

一尘不染

下面的示例是理解为什么不允许这样做的最简单方法:

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());

最后一条语句将破坏.NET的类型安全性。

但是,数组允许这样做:

Fruit[] fruits = new Apple[10]; // This is perfectly fine

然而,把一个Banana进入fruits还是会破坏类型安全,所以为此.NET必须做对每一个阵列插入类型检查,并抛出一个异常,如果它不是真正的Apple。这可能对性能造成很小的影响,但是可以通过struct围绕这两种类型创建包装器来规避,因为对于值类型不会执行此检查(因为它们不能从任何东西继承)。刚开始,我不明白为什么要做出这个决定,但是您会经常遇到为什么这样做很有用。最常见的是String.Format,它可以接受params object[]任何数组。

但是,在.NET 4中,有一个类型为safe covariance /
contravariance的安全协方差/协方差,您可以进行这样的分配,但前提是必须证明它们是安全的。有什么可证明的安全性?

IEnumerable<Fruit> fruits = new List<Apple>();

以上在.NET 4中起作用,因为IEnumerable<T>成为了IEnumerable<out T>。在out该手段T只能永远都 出来
fruits,并且有 没有一种方法在所有IEnumerable<out T>有史以来采用T的参数,这样你就可以永远正确传递Banana IEnumerable<Fruit>

矛盾几乎是相同的,但我总是忘记它的确切细节。毫不奇怪,为此,现在in在类型参数上使用了关键字。

2020-05-19