2011-06-01 163 views
6

我大致明白接口,繼承和多態,但有一件事我不解。的IList <T>和列表<T>轉換與接口

在這個例子中,實現IAnimal當然名單實現IList的

IList<IAnimal> cats = new List<Cat>(); 

,但它會產生一個編譯錯誤(無法隱式轉換類型...) 。如果我使用Cat繼承的asbtract超類[動物],它也不起作用。但是,如果我有更換IAnimal

IList<Cat> cats = new List<Cat>(); 

它編譯罰款。

在我心中,因爲實現IAnimal,第一個例子應該是可以接受的,讓我們返回一個接口列表和所包含的類型兩者。

誰能解釋爲什麼它是不是有效?我確定有一個合理的解釋。

回答

20

有一個合理的解釋,並就這個問題詢問每天都問差不多在計算器上。

假設這是合法的:

IList<IAnimal> cats = new List<Cat>(); 

這是什麼被法律停止?

cats.Add(new Giraffe()); 

沒有。 「貓」是動物的列表,而長頸鹿是動物,因此你可以將一隻長頸鹿添加到貓的列表中。

顯然這不是類型安全的。

在C#4中,我們添加了一個功能,如果元數據註釋允許編譯器證明它是類型安全的,則可以這樣做。在C#4你可以這樣做:

IEnumerable<IAnimal> cats = new List<Cat>(); 

因爲IEnumerable<IAnimal>沒有Add方法,所以沒有辦法違反類型安全。

查看我的一系列關於如何在C#4中設計此功能的文章以獲取更多詳細信息。

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

(開始從底部。)

+2

+1:內容豐富且和往常一樣有用:) – Juliet 2011-06-01 16:26:05

+0

謝謝埃裏克。我一定會閱讀你的文章。還要問一個不斷重複的問題......下次會做更好的搜索。 – Curtmantle 2011-06-01 16:32:04

+2

@Mark:不客氣,不用擔心;事實上,這個問題被問到這麼多是促使首先將功能添加到C#4的原因之一。很明顯,人們有直覺認爲一般方差應該是類型系統的一部分。現在只是教育人們關於什麼樣的差異可證明是安全的。 – 2011-06-01 16:36:00

1

這種類型的covariance不支持在C#4.0。期望你想要的行爲是合理的,但它不被支持(現在)。

+2

你能澄清嗎? C#4.0支持協變和逆變,但IList 不是協變接口。 – 2011-06-01 16:16:17

+0

哈哈,好吧,Eric Lippert只是回答了,所以我認爲總結一下吧:D – 2011-06-01 16:24:52

1

可以實現,使用LINQ:

IList<IAnimal> cats = new List<Cat>().Cast<IAnimal>(); 
6

C#不支持這種變化對IList<T>的類型安全的原因。

如果C#也支持這一點,你會想到會在這裏出現呢?

IList<IAnimal> cats = new List<Cat>(); 

cats.Add(new Dog());   // a dog is an IAnimal too 
cats.Add(new Squirrel()); // and so is a squirrel 

在C#4你能夠做這樣的事:

IEnumerable<IAnimal> cats = new List<Cat>(); 

這是因爲IEnumerable<T>接口做這種支持差異。一個IEnumerable<T>是隻讀序列,所以沒有辦法,你可以隨後添加DogSquirrelIEnumerable<IAnimal>這實際上是的Cat列表。

1

IList<T>不是協變接口(或這將是IList<out T>)。這是因爲IList都將T作爲參數,並將其作爲方法的返回值返回,這使得協方差成爲問題。

例如,如果在你的榜樣:

IList<IAnimal> cats = new List<Cat>();

您想添加一個新的貓,從貓,它將使:

cats.Add(new Dog());

假設狗還實施IAnimal ,這顯然是不正確的,並且不起作用。這就是爲什麼IList不是協變或逆變界面。

0

如果你需要一個類似列表的接口上的協方差和逆變,你應該定義一個接口IReadableList <出牛逼>和IWritableList < T中>,並從列表<牛逼>它實現了兩個ReadableList <牛逼&派生型和WriteableList <T>。這將使得有可能將NewList <Cat>傳遞給期望可讀列表<動物>或可寫列表<SiameseCat>的例程。

相關問題