2015-12-11 49 views
9

在下面的示例代碼,爲什麼調用ArrayMethod失敗的genric類型時我不包括class約束傳遞一個數組

public interface IFoo { } 
public interface IBar : IFoo { } 

public class Test 
{ 
    public void ClassConstraintTest<T>() where T : class, IFoo 
    { 
     T[] variable = new T[0]; 
     ArrayMethod(variable); 
    } 

    public void GenericTest<T>() where T : IFoo 
    { 
     T[] variable = new T[0]; 
     ArrayMethod(variable); // Compilation error: Can't convert T[] to IFoo[] 
    } 

    public void InheritanceTest() 
    { 
     IBar[] variable = new IBar[0]; 
     ArrayMethod(variable); 
    } 

    public void ArrayMethod(IFoo[] args) { } 
} 

回答

9

是因爲array covariance,即事實MySubtype[]MyType[]的子類型,僅適用於參考類型。 class約束確保T是引用類型。

(需要注意的是,現在回想起來,陣列協方差爲considered to be a bad idea嘗試,如果你能避免它,例如,通過使ArrayMethod通用或使用IEnumerable<IFoo>代替。)

+0

一個好的類型系統應該有一個協變類型,它可以用作一個數組,它的項目可以在數組中被讀取,交換或複製。 .NET和Java中的數組類型可以安全地以協變方式用作這種類型,並且可以以非協變方式安全地用作可自由寫入的集合。也許最好是有多種引用數組,有不同的廣告能力,但恕我直言,那些簡單地說,協變陣列是一個「錯誤」的人並沒有真正計算出替代品的成本。 – supercat

5

簡而言之:數組協方差只能當兩個陣列都是參考(class)類型。

要理解這一點,您必須瞭解不同類型數組的內存佈局。在C#,我們有值陣列(int[]float[]DateTime[],任何用戶定義struct[]),其中每個事物被存儲在數組內依次和參考陣列(object[]string[],任何用戶定義class[]interface[],或delegate[]),其中引用按順序存儲在數組中,並且對象存儲在數組的外部,而不管它們在內存中的位置。

當您請求上的任何T方法工作(不: class約束)你允許任一這兩個類型的陣列,但是編譯器知道一個事實,即任何int[](或任何其他值陣列)不會以某種方式神奇地成爲一個有效的IFoo[](或任何其他參考數組),並禁止轉換。即使您的結構沒有其他原因實現了IFoo,但IFoo[]是一個參考陣列,並且T[]將會是一個值數組。

但是,當您指定T是引用類型(即class聲明),現在可能是T[]是一個有效的IFoo[],因爲它們都是參考陣列。因此,編譯器允許使用陣列協方差規則的代碼(其中陳述您可以使用T[],其中IFoo[]是必需的,因爲TIFoo的子類型)。