通用协方差编译时安全检查

时间:2016-09-20 22:46:18

标签: c# generics covariance

List<T>T不协变的原因,而IEnumerable<T> {/ 1>}上的协变的原因通常通过以下示例说明此

鉴于以下类别:

T

允许以下内容:

public class Fruit
{
}

public class Apple : Fruit
{
}

public class Banana : Fruit
{
}

以下是不允许的:

public void Permitted()
{
    IEnumerable<Fruit> bananas = new List<Banana>
    {
        new Banana(),
        new Banana(),
        new Banana(),
    };

    foreach (Fruit banana in bananas)
    {
        // This is all good, because a banana "is a" fruit and
        // we can treat it as such.
    }
}

但是,我们仍然可以通过以下方式实现这一目标:

public void Disallowed()
{
    // Compiler rejects this!
    List<Fruit> bananas = new List<Banana>
    {
        new Banana(),
        new Banana(),
        new Banana(),
    };

    // ...Otherwise we can add an apple to a list containing bananas
    bananas.Add(new Apple());
}

当然,我们可以这样做:

public void Loophole()
{
    // Compiler is happy again
    IEnumerable<Fruit> bananas = new List<Banana>
    {
        new Banana(),
        new Banana(),
        new Banana(),
    };

    // ...And now we can add an apple to a list of bananas
    bananas.ToList().Add(new Apple());
}

public void AlsoAllowed() { var fruit = new List<Fruit>(); fruit.Add(new Apple()); fruit.Add(new Banana()); } 不协变的常见论据(根据我的理解)是这样做可以让我们将任意基础对象添加到包含派生对象的集合中。也许这是过于简单化,但不是上面的例子正在做什么?

1 个答案:

答案 0 :(得分:4)

当您执行bananas.ToList().Add(new Apple())时,bananas.ToList()会创建List<Fruit>。这是一种能够包含任何类型水果的列表类型。 new Apple()可以添加到该列表的事实是有道理的。

bananas的类型为List<Banana>,这是一种只能包含香蕉的列表类型。无法将new Apple()添加到此列表中,并且您的示例不会new Apple()添加到此列表中。您的示例创建了一个更宽容的列表,并添加到该列表中,但保留原始列表未修改。