2013-08-24 75 views
2

參照在Visual Studio編譯爲C#這個測試代碼2010表示逆變上約束一般類型參數

public class Test 
{ 
    class Base { } 
    class Derived : Base { } 

    void Test1(IEnumerable<Derived> derived) 
    { 
     IEnumerable<Base> b = derived; //This works fine using covariance on IEnumerable 
    } 

    void Test2<TDerived, TBase>(TDerived derived) where TDerived : TBase 
    { 
     TBase b = derived; //This works fine because TDerived is constrained to derive from TBase 
    } 

    void Test3<TDerived, TBase>(IEnumerable<TDerived> derived) where TDerived : TBase 
    { 
     IEnumerable<TBase> b = derived; //ERROR: paraphrased: Cannot implicitly convert type IEnumerable<TDerived> to IEnumerable<TBase> 
    } 
} 

我試圖利用的IEnumerable的協方差來存儲內的可枚舉的通用類型參數的可枚舉類型參數受限於繼承的類。 Test3就是一個例子。請注意,Test1和Test2(分別展示編譯時類型和約束類型的賦值的協方差)都可以很好地編譯。這是兩種語言功能的組合,它不適合我。

我能夠使用IEnumerable<TBase> b = derived.Cast<TBase>()並且100%確信如果我的理解沒有缺陷,沒有演員將會失敗,所以我確實有一個解決方法可用。我的問題是,爲什麼編譯器不允許這樣做?這是禁止出於某種邏輯原因,編譯器中的疏忽,還是其他我沒有想到的東西?

+2

喬恩斯基特已經給出了答案。 'IEnumerable '*是*協變的,但協方差只適用於C#中的引用類型。例如,如果'der'是一個'IEnumerable ',那麼賦值'IEnumerable b = der;'將不被允許。所以'Test3 (der);'會是一個問題,即使'int'是一個'IFormattable'。 –

回答

10

答到最初的問題

您目前正在試圖TDerived類型的元素轉換爲Base類型的序列。我不希望你的Cast電話工作,因爲TDerived不實施IEnumerable - 我懷疑你實際上是在不同的情況下工作。

我懷疑你實際上意味着:

void Test3<TDerived>(IEnumerable<TDerived> derived) where TDerived : Base 
{ 
    IEnumerable<Base> b = derived; 
} 

這沒有問題編譯。

答到編輯的問題

好了,現在我們已經得到了真正的問題之間有兩個類型參數的問題是,編譯器不知道他們是引用類型 - 這是通用方差所需。您可以修復與class約束上TDerived

void Test3<TDerived, TBase>(IEnumerable<TDerived> derived) 
    where TDerived : class, TBase 
{ 
    IEnumerable<TBase> b = derived; 
} 
+0

你絕對正確。但是我實際上犯了這個錯誤,試圖將我的問題簡化爲最簡單的形式。我已經修改了我的問題,以反映對我而言仍然失敗的更復雜的案例。這是基類和派生類都是泛型類型參數的情況。 –

+0

@ShaneTapp:這就是爲什麼提問時要小心,所以你不會浪費人的時間。 (不僅僅是我,還有其他人在編輯之前看過這個問題。)我編輯了我的答案,以反映問題的變化。 –