为什么我不能将List <Derived>分配给List <Base>?

22 .net c# generics covariance

我定义了以下类:

public abstract class AbstractPackageCall
    {

     ...

    }
Run Code Online (Sandbox Code Playgroud)

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

class PackageCall : AbstractPackageCall
    {

      ...
    }
Run Code Online (Sandbox Code Playgroud)

还有其他几个子节 AbstractPackageCall

现在我想进行以下调用:

 List<AbstractPackageCall> calls = package.getCalls();
Run Code Online (Sandbox Code Playgroud)

但我总是得到这个例外:

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>' 
Run Code Online (Sandbox Code Playgroud)

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

 internal List<PackageCall> getCalls()
        {
            return calls;
        }
Run Code Online (Sandbox Code Playgroud)

Jul*_*anR 55

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

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());
Run Code Online (Sandbox Code Playgroud)

最后一句话会破坏.NET的类型安全性.

然而,数组允许这样做:

Fruit[] fruits = new Apple[10]; // This is perfectly fine
Run Code Online (Sandbox Code Playgroud)

然而,把一个Bananafruits仍破类型安全,所以为此.NET必须做对每一个阵列插入类型检查,并抛出一个异常,如果它不是真正的Apple.这可能是(小)性能损失,但这可以通过struct在任一类型周围创建包装来规避,因为这种检查不会发生在值类型中(因为它们不能从任何东西继承).起初,我不明白为什么做出这个决定,但你会经常遇到为什么这个有用.最常见的是String.Format,可以将params object[]任何数组传递给它.

但是在.NET 4中,存在类型安全的协方差/逆变,它允许您进行类似的这些分配,但前提是它们可证明是安全的.什么是可靠的安全?

IEnumerable<Fruit> fruits = new List<Apple>();
Run Code Online (Sandbox Code Playgroud)

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

逆差异大致相同,但我总是忘记它的具体细节.不出所料,现在in关于类型参数的关键字.

  • 为什么选择downvote?如果答案不是愚蠢,争论或冒犯,总是留下评论,以便我可以修复/纠正事情. (18认同)