下面的調用順序會引發循環以拋出一個ArrayTypeMismatch例外:
D[] array = new D[]{
new D(),
new D(),
new D(),
new D(),
new D(),
new D(),
new D(),
new D(),
new D(),
new D()};
DestroyCollection(array);
當我們將兩個板塊集合起來看時就一目了然了。調用頁面創建了一個D 對象數組,然後調用了期望B對象數組的方法。因為數組是可變的,你可以將D[]發送到期望B[]的方法。但是在DestroyCollection()裡面,可以修改數組。在本例中,它創建了用於集合的新對象,類型D2的對象。這在該方法中是允許的:D2對象可以保存在B[]中因為D2是由B衍生出來的。但是其結合往往會引發錯誤。當你引入一些返回數組儲存的方法並視其為逆變值時,同樣的事情也會發生。向這樣的代碼才能有效:
B[] storage = GenerateCollection();
storage[0] = new B();
但是,如果GenerateCollection的內容向這樣的話,那麼當storage[0]要素被設置到B對象中,它會引發ArrayTypeMismatch異常。
泛型集合
數組被當作是可變和可逆變,即便是不安全的。.NET1.x集合類型是不可變的,但是將參考保存到了Systems.Object。.Net2.x中的泛型集合並且被視為不可變。這意味著你不能夠替代包含有較多衍生對象的集合。最好你試一試下面的代碼:
private void WriteItems(IEnumerable< object> sequence)
{
foreach (var item in sequence)
Console.WriteLine(item);
}
你要知道自己可能會和其他執行IEnumberable< T>集合一起對其進行調用因為任何T必須由對象衍生。這或許是你的期望,但是由於泛型是不變的,下面的操作將無法進行編譯:
IEnumerable< int> items = Enumerable.Range(1, 50);
WriteItems(items); // generates CS1502, CS1503
你也不能將泛型集合類型視為可逆變。這行代碼之所以不能進行編譯是因為分配返回數值的時候,你不能將IEnumberable< T>轉換成IEnumberable< object>:
IEnumerable< object> moreItems =
Enumerable.Range(1, 50);
你或許認為IEnumberable< int>衍生自IEnumberable< object>,但是事實不然。IEnumberable< int>是一個基於IEnumberable< T>泛型類定義的閉合泛型類。它們不會相互衍生,因此沒有關聯性,而且你也不能視其具有可變性。即便在兩個類型參數之間具備關聯性,使用類型參數的泛型類型不會對這種關聯有響應。
C#以不變的方式對待泛型顯示出了該語言的強大優勢。最重要的是,你不能在數組和1.x集合中出錯。一旦你編譯了泛型代碼,你就能夠很好地利用這些代碼了。這與C#的傳統具有一致性,因為它利用了編譯器來刪除代碼中可能存在的漏洞。
但是對於對於強效輸入的依賴性顯示出了一定的局限性。上文顯示的關於泛型轉換的構造看上去是有效的。但是你不會想將其轉換為.Net1.x集合和數組中使用的行為。我們真正想要的是僅在它運行的時候將泛型類型視作是可變的或可逆變的,而不是用運行時錯誤代替編譯時錯誤的時候。