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());
}
不协变的常见论据(根据我的理解)是这样做可以让我们将任意基础对象添加到包含派生对象的集合中。也许这是过于简单化,但不是上面的例子正在做什么?
答案 0 :(得分:4)
当您执行bananas.ToList().Add(new Apple())
时,bananas.ToList()
会创建List<Fruit>
。这是一种能够包含任何类型水果的列表类型。 new Apple()
可以添加到该列表的事实是有道理的。
bananas
的类型为List<Banana>
,这是一种只能包含香蕉的列表类型。无法将new Apple()
添加到此列表中,并且您的示例不会将new Apple()
添加到此列表中。您的示例创建了一个更宽容的列表,并添加到该列表中,但保留原始列表未修改。