2016-09-20 42 views
0

爲什麼List<T>不上T協變的原因,而IEnumerable<T>協對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()); 
} 

List<T>不是協變的普遍的說法(按照我的理解)是,這樣做將使我們能夠任意基本對象添加到包含派生對象的集合。也許這是一種過度簡單化,但是這不是說上面的例子在做什麼?

回答

4

當你做bananas.ToList().Add(new Apple())時,bananas.ToList()創建一個List<Fruit>。這是一種能夠包含任何類型水果的列表類型。 new Apple()可以添加到該列表的事實是有道理的。

bananas有類型List<Banana>,這是一個只能包含香蕉的列表類型。沒有辦法將new Apple()添加到此列表中,並且您的示例不會new Apple()添加到此列表中。您的示例創建了一個更寬容的列表,並添加到該列表中,但未修改原始列表。

+0

只是爲了補充一點,在第二個例子中,將'Apple'和'Banana'添加到'fruit'絕對沒有任何問題,它不僅被輸入爲'List ',而且被實例化。 –

+0

現在很明顯,你提到它 - 謝謝! – LeopardSkinPillBoxHat