2012-09-11 289 views
8

我只是沒有得到的東西在.NET泛型類型鑄造。 有人可以解釋下面的代碼片段中會發生什麼嗎?泛型類型鑄造

void Main() 
{ 
    IEnumerable<int> ints = new List<int>(); 
    IEnumerable<string> strings = new List<string>(); 

    var rez1=(IEnumerable<object>)ints; //runtime error 
    var rez2=(IEnumerable<object>)strings; //works 
    var rez3=(List<object>)strings; //runtime error 
} 

回答

11

讓我們先從這是最簡單的的第二行。

由於IEnumerable<T>的類型參數現在爲covariant(這就是out T中的out所做的),所以該鑄件可以工作。這意味着你可以自由投射IEnumerable<Derived>IEnumerable<Base>

第一行,這似乎是相同的情況下,不工作,因爲int是值類型。接口方差根本不適用於值類型,因爲值類型並不真正從System.Object繼承;他們可以將盒裝轉換成object,但這不一樣。文檔提到

差異僅適用於引用類型;如果您爲變體類型參數指定值類型 ,則該類型參數對於結果構造類型爲 不變。

最後,第三個行不起作用,因爲List<T>類型參數是不變。你可以看到它的類型參數沒有out;規則不允許因爲List<T>不是接口:

在.NET Framework 4,變體類型參數被限制爲 通用接口和通用委託類型。

+0

但Int32可以轉換爲Object。這不足以作出協變嗎?或者它是一個編譯器功能? – Vasaka

+0

@Vasaka:這是一個裝箱操作,而不是參考轉換。這是技術性的,但是儘管編譯器在每種情況下都具有相同的語法,但完全不同。 – Jon

+0

...正如所有泛型類和結構的類型參數一樣;僅支持接口和委託類型的類型差異。 – phoog

0

每從System.ValueType導出,具有System.Enum異常類型的定義,實際上定義了兩種類型的東西:一個堆對象類型,以及一個存儲位置類型。後者的實例可以隱含地轉換爲前者(製作其中包含的數據的副本),前者的實例可以明確地向後者的類型轉換(同樣);即使這兩種東西都是由相同的System.Type描述的,雖然它們具有相同的成員,但它們的行爲卻非常不同。

List<AnyClassType>將預期保持一堆堆對象引用的;有問題的列表是否是List<String>List<StringBuilder>List<Button>,或什麼的,可能會感興趣的名單的用戶,但不是真正感興趣的List<T>本身。如果一個人蒙上了List<Button>IEnumerable<Control>,別人誰調用它的GetEnumerator()方法會希望得到一個對象,它將對從Control獲得堆對象輸出參考;從List<Button>.GetEnumerator()回報將滿足這一期望。相比之下,如果有人將List<Int32>轉換爲List<Object>,那麼調用GetEnumerator()的人會期望可以輸出堆對象引用的內容,但List<Integer>.GetEnumerator將會產生輸出值類型整數的內容。

可以將Int32值存儲到List<Object>List<ValueType>;將整數存儲到這樣的列表將會將其轉換爲其堆對象形式並存儲對其的引用;調用GetEnumerator()會產生輸出堆引用的東西。但是,沒有辦法指定這樣的列表將僅包含對應於Int32的堆類型的實例。在C++/CLI中,可以聲明類型爲「引用堆存儲的值類型」的變量,但.net中的泛型類型背後的機制不能用於這種類型。