C# IList<;T>;vs IEnumerable<;T>;:为什么这个任务无效?

C# IList<;T>;vs IEnumerable<;T>;:为什么这个任务无效?,c#,ienumerable,C#,Ienumerable,大象:动物 IList<Elephant> elephants = new List<Elephant>(); IEnumerable<Animal> animalList = elephants; IList=新列表(); IEnumerable animalist=大象; 这很好,但是 IList<Elephant> elephants = new List<Elephant>(); IList<Animal> an

大象:动物

IList<Elephant> elephants = new List<Elephant>();
IEnumerable<Animal> animalList = elephants;
IList=新列表();
IEnumerable animalist=大象;
这很好,但是

IList<Elephant> elephants = new List<Elephant>();
IList<Animal> animalList = elephants;
IList=新列表();
IList animalList=大象;
抛出一个错误。为什么呢

如果

IList<Elephant> elephants = new List<Elephant>();
IList<Animal> animalList = elephants;
但是
animalList
实际上是指与
大象
相同的参照物。由于
大象
是一个
IList
,您将尝试向只能包含
大象
实例的集合中添加
动物
的实例

之所以可以使用
IEnumerable
,是因为
IEnumerable
位于类型参数
T
中。这是因为
T
仅出现在接口
IEnumerable
中的“out”位置。由于您只使用
IEnumerable
中的
T
实例,因此可以安全地将
IEnumerable
实例分配给
IEnumerable
类型的变量。任何来自
IEnumerable
的东西都是
Base
,这就是为什么实现
IEnumerable
的东西可以安全地用作
IEnumerable
的原因。这就是为什么将
IEnumerable
的实例分配给
IEnumerable
类型的变量是安全的

但是您在使用
IList
时遇到了麻烦,因为
IList
在类型参数
T
中是不协变的。这里的问题是
T
出现在界面
IList
的“in”位置。因此,如果您可以将
IList
的实例分配给
IList
类型的变量,那么您可以尝试将
Base
的实例粘贴到
IList
中,这实际上是试图将
Base
的实例粘贴到
IList
的实例中,这显然是不安全的。这正是我们刚才在使用
IList
IList
时遇到的问题


请注意,
IList
在类型参数
T
中也不是逆变的。这是因为
T
出现在界面
IList
的“out”位置。如果您可以将
IList
的实例分配给
IList
类型的变量,那么您可以尝试从
IList
中获取
Derived
的实例,这将试图从
IList
的实例中获取
Derived
,这显然是荒谬的。

这被称为协方差反方差


我不会详细介绍,但您可以。

请注意,
Derived[]
可以用作
Base[]
,插入
Derived
将引发异常。这是因为.NET继承了数组variance周围被破坏的Java规则
animalList.Add(new Animal());