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

C# 通用协方差编译时安全检查,c#,generics,covariance,C#,Generics,Covariance,List在t上不协变,而IEnumerable在t上协变的原因通常通过这样的示例来说明 鉴于以下类别: public class Fruit { } public class Apple : Fruit { } public class Banana : Fruit { } 以下是允许的: public void Permitted() { IEnumerable<Fruit> bananas = new List<Banana> {

List
t
上不协变,而
IEnumerable
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.
    }
}
允许公开作废()
{
IEnumerable香蕉=新列表
{
新香蕉(),
新香蕉(),
新香蕉(),
};
foreach(香蕉中的水果香蕉)
{
//这一切都很好,因为香蕉是一种水果和水果
//我们可以这样对待它。
}
}
以下是不允许的:

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漏洞()
{
//我又高兴了
IEnumerable香蕉=新列表
{
新香蕉(),
新香蕉(),
新香蕉(),
};
//…现在我们可以在香蕉列表中添加一个苹果
香蕉.ToList().Add(新苹果());
}
当然,我们可以这样做:

public void AlsoAllowed()
{
    var fruit = new List<Fruit>();
    fruit.Add(new Apple());
    fruit.Add(new Banana());
}
public void也被允许()
{
var fruit=新列表();
添加(新苹果());
水果。添加(新香蕉());
}

List
不协变(据我理解)的常见理由是,这样做将允许我们向包含派生对象的集合中添加任意基本对象。也许这过于简单化了,但上面的例子不就是这么做的吗?

当你做
香蕉时,ToList().Add(new Apple())
香蕉。ToList()
创建一个
列表。这是一个列表类型,可以包含任何类型的水果。
newapple()
可以添加到该列表中这一事实是有道理的


香蕉
具有类型
列表
,该列表类型只能包含香蕉。无法将
new Apple()
添加到此列表中,您的示例也不会将
new Apple()
添加到此列表中。您的示例创建了一个更宽容的列表,并添加到该列表中,但保留原始列表不变。

当您执行
香蕉时,ToList().add(new Apple())
香蕉。ToList()
创建一个
列表。这是一个列表类型,可以包含任何类型的水果。
newapple()
可以添加到该列表中这一事实是有道理的


香蕉
具有类型
列表
,该列表类型只能包含香蕉。无法将
new Apple()
添加到此列表中,您的示例也不会将
new Apple()
添加到此列表中。您的示例创建了一个更宽容的列表,并将其添加到该列表中,但保留原始列表不变。

仅添加到该列表中,在第二个示例中,将
苹果
s和
香蕉
s添加到
水果
中绝对没有错。水果不仅被键入,而且被实例化为
列表
,在第二个示例中,将
Apple
s和
Banana
s添加到
fruit
中绝对没有错,后者不仅被键入,而且被实例化为
列表。