2010-10-27 52 views
14

在下面的代码:问题关于C#的协方差

interface I1 { } 
class CI1: I1 { } 

List<CI1> listOfCI1 = new List<CI1>(); 

IEnumerable<I1> enumerableOfI1 = listOfCI1; //this works 

IList<I1> listofI1 = listOfCI1; //this does not 

我能够分配我的“listOfCI1”到IEnumerable<I1>(由于协方差)

但为什么我不能把它分配给一个IList<I1>? 对于这个问题,我甚至不能做到以下几点:

List<I1> listOfI12 = listOfCI1; 

不应该协方差让我分配一个派生类型的基本类型?

回答

24

简而言之,IList<T>不是协变的,而IEnumerable<T>是。这是为什么...

假设IList<T>协变。下面的代码显然不是类型安全的......但是你想在哪里出错?

IList<Apple> apples = new List<Apple>(); 
IList<Fruit> fruitBasket = apples; 
fruitBasket.Add(new Banana()); // Aargh! Added a Banana to a bunch of Apples! 
Apple apple = apples[0]; // This should be okay, but wouldn't be 

对于很多方差细节,看到它埃里克利珀的blog post series,或者看我说说从NDC方差video

基本上,只有在保证安全的情况下才允许方差(并且以保留表示的方式,这就是为什么您不能将IEnumerable<int>转换为IEnumerable<object> - 装箱转换不会保留表示形式)的原因。

+0

顺便说一句,那话很棒。 – 2010-10-27 14:51:22

+0

@Arnis:谢谢 - 我自己喜欢它,即使我没有展示我录制的Hokey Cokey视频... – 2010-10-27 14:52:20

+2

Jon,BCL团队也加入了IEnumerable(IEnumerable :IEnumerable),因为IEnumerable不允许你添加任何新成员(即它的不可变)使其安全。但另一方面,IList 没有添加“out”,因为它是可变的(可以在底层列表中添加不同的实现?) – 2010-10-27 17:46:31

3

否则,你然后能添加不同的实施I1到应该只包含C1的List。

1

IList<T>接口不协变。

5

比较声明(MSDN)

public interface IEnumerable<out T> : IEnumerable 

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable 

你看到神奇的词,out?这意味着协方差是开启的。

+3

更具体地说,这意味着'IEnumerable '对于'T' *是协变的。一个接口在某些类型参数中可能是变体的,但其他接口可能是变体的 – 2010-10-27 14:49:10