2016-07-17 211 views
1

我碰到一個案例,List.AsReadOnly()返回ReadOnlyCollection而不是IReadOnlyCollection這個事實讓我感到困難。由於返回的收藏是的Value,因此無法自動上傳到IReadOnlyCollection。這看起來很奇怪,經過對.Net源代碼的審查,我確認AsReadOnly()方法爲List做了一些不同於Dictionary的操作,即返回具體類而不是接口。爲什麼List.AsReadOnly返回一個ReadOnlyCollection,但Dictionary.AsReadOnly返回一個IReadOnlyDictionary?

任何人都可以解釋爲什麼這是?這種不一致性似乎是不利的,特別是因爲我們希望在可能的情況下使用接口,特別是在公開時。

在我的代碼中,起初我在想,既然我的客戶只是一個私有方法,我可以將其參數簽名從IReadOnlyDictionary<T, IReadOnlyCollection<T>>更改爲IReadOnlyDictionary<T, ReadOnlyCollection<T>>。但是,後來我意識到,這使它看起來像私有方法可能會修改收藏價值,所以我把一個惱人的顯式轉換到前面的代碼,以便正確使用該接口:

.ToDictionary(
    item => item, 
    item => (IReadOnlyCollection<T>) relatedItemsSelector(item) 
     .ToList() 
     .AsReadOnly() // Didn't expect to need the direct cast 
) 

哦,因爲我總是得到協變和矛盾的困惑,有人可以告訴我哪一個阻止自動演員,並試圖以合理的方式提醒我如何記住他們的未來? (例如,對於_____ [輸入/輸出]參數,集合不是______variant [co/contra])。我明白爲什麼不能這樣做,因爲接口可能有很多實現,並且不能安全地將所有字典中的單個元素添加到請求的類型。除非我吹這個簡單的方面,我不知道瞭解它,在這種情況下,我希望你能幫助我設置正確的...

回答

0

原因是歷史。在.NET 4.5中添加了IReadOnly *接口,而在.NET 2.0中添加了List<T>.AsReadOnly()。改變它的返回類型將是一個突破性的改變。

顯式轉換不是那麼糟糕。它甚至不是一個運行時轉換,因爲編譯器可以靜態驗證它(不會發射到IL)。順便說一下,你可以將它轉換爲IReadOnlyList<T>,它也提供對列表的索引訪問。您也可以編寫一個擴展方法來返回您需要的類型(例如AsReadOnlyList())。

關於{合作,禁忌}變化,我覺得它更容易使用C#的關鍵字in(逆變)和out(協)記得。 in類型參數只能顯示爲輸入方法參數,而out類型參數只能顯示爲輸出(返回值)。接受參數的方法,例如類型爲Base,可以安全地使用類型Derived來調用,因此可以安全地將in參數轉換爲該方向。 out恰恰相反。

例如:

interface IIn<in T> { Set(T value); } 
IIn<Base> b = ... 
IIn<Derived> d = b; 
d.Set(derived); // safe since any method accepting Base can handle Derived 

interface IOut<out T> { T Get(); } 
IOut<Derived> d = ... 
IOut<Base> b = d; 
b.Get(); // safe since any Derived is Base 

非只讀集合接口不能* -variant,因爲他們不得不既inout,這將是不安全的。編譯器和CLR不允許這樣做。 。NET確實與陣列存在不安全差異:

var a = new[] { "s" }; 
var o = (object[])a; 
o[0] = 1; // ArrayTypeMismatchException 

您可以看到他們希望如何避免泛型差異。據推測,他們可以添加允許in(cotravariant)方向的只寫接口,但我猜測他們在這方面沒有發現很多價值。

+0

哦,哦,哦!我假設它不會自動轉換,因爲它*不是*協變的,但現在我明白,因爲IReadOnlyCollection是隻讀的,編譯器可以看到從派生類型轉換爲基類是安全的。所以如果是這樣的話,爲什麼我必須做一個明確的演員? – ErikE

+0

只有當您在兩個通用*接口*之間進行投射並更改*類型參數*(從基礎到派生或反之亦然)時纔會發生協變和逆變。您使用的演員陣容只是一個陣容 - 這是允許的,因爲'ReadOnlyCollection '實施'IReadOnlyList '。 –

+0

哦,哎呀,這很有道理。它越來越清晰。爲什麼它不能隱式施放? – ErikE

相關問題