2010-10-27 75 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