2014-04-23 65 views
2

爲什麼?當然,我不需要用戶定義的轉換,因爲List(T)IList(T)HashSet(T)IEnumerable(T)。謝謝。將嵌套集合轉換爲接口

不能將類型'System.Collections.Generic.List<System.Collections.Generic.HashSet<string>>'隱式轉換爲'System.Collections.Generic.IList<System.Collections.Generic.IEnumerable<string>>'。一個顯式轉換存在(是否缺少強制轉換?)

class Program { 

    static IList<IEnumerable<string>> GetSet() { 
     return new List<HashSet<string>>(); 
    } 

} 
+8

因爲'IList'是不變的(或者更具體地說,它不是協)在其類型參數。如果該方法的聲明返回類型是,例如,「IEnumerable >'',您可以返回'List >'。 –

+2

換句話說:問題是關於什麼可以傳遞到列表的方法有衝突的規則。 'IList >'聲明你可以用任何'IEnumerable '調用'Add',但是'List >'說它必須是一個HashSet。但是,您可以創建一個'List >',並將HashSets添加到它。 – nmclean

回答

1

因爲IList<T>是不變的。

爲了說明爲什麼這是一個問題,請看下面的例子:

IList<T>提供了實例Add(T object)IEnumerable<string>的方法,這將與你的構造表達new List<HashSet<string>>()衝突。這意味着我可以打電話給你的program.GetSet()並添加一個new ArrayList<string>(),但是你構建的實例不允許它,因爲它有一個合約,它只包含HashSet<string>實例(當你詢問包含ArrayList<T>GetSet()的內容時你會返回什麼?

的typeparameter是雙重嵌套沒關係。比如IList<Object>不是IList<FooClass>的超任的事實。


這不是IEnumerable<T>的情況下, self(意思是IEnumerable<T>也是IEnumerable<SuperT>),因爲IEnumerable<T>的唯一功能是輸出值。根據Liskov替代原則允許使用。

原理說,在一個類層次走下來的時候,在 返回類型只能成爲更普遍的(超類/接口) 和參數類型只能變得更加具體(子 類/接口) 。


C#提供了工具來處理這個原理(所謂方差)通過在泛型類型聲明使用inout關鍵字。

例如,如果你確信一個Foo<SubT>Foo<T>還有,你可以這樣定義:

public class Foo<out T> { 

    T getResult() { 
     //do something 
    } 

} 

在這種情況下T是協變對於Foo。如果類型參數僅用作輸入,則可以進一步指定。例如Bar<T>顯然是Bar<SubT>下面定義的特殊情況:

public class Bar<in T> { 

    void setParameter(T parameter) { 
     //do something 
    } 

} 
2

我想我真的需要是變化的一個簡單的解釋 - 逆變和協方差 - 在C#泛型的情況下,我已經找到herehere

錯誤消息並沒有真正使我說,但現在我會總結:

逆變

通用類,當然是一個類的模板而不是類定義,可以使用逆向使用關鍵字。甲逆變類允許從基類的實例分配到一個派生類實例即邊境牧羊犬=犬

public interface AllowAssignmentsFromBaseToDerived<in T> 

協方差

泛型類可以使用關鍵字進行協變。甲協變類允許從派生類實例分配到基類的實例,即狗=邊境牧羊犬

public interface AllowAssignmentsFromDerivedToBase<out T> 

方差被支承用於數組類型,因爲C#1.0和委託類型,因爲C#2.0,以及因爲C#通用類型參數4.0。

很高興能有更多的回覆來覆蓋我已經錯過的更多觀點,仍然感覺有點不知情。

更多信息從here解禁:

如何創建變型通用接口和委託自己?

out關鍵字將類型參數標記爲協變,並且 關鍵字將其標記爲逆變。到 兩個最重要的規則要記住:

  1. 您可以標記一個泛型類型參數的協變如果只是 作爲方法的返回類型,而不是作爲一種形式化方法 參數。

  2. 反之亦然,你可以,如果它 只是用來作爲一種形式化方法的參數,而不是用作 方法的返回類型標記類型逆變。

interface IVariant<out R, in A> 
    { 
     // These methods satisfy the rules. 
     R GetR(); 
     void SetA(A sampleArg); 
     R GetRSetA(A sampleArg); 

     // And these don’t. 
     // A GetA(); 
     // void SetR(R sampleArg); 
     // A GetASetR(R sampleArg); 
    } 

此外,如果擴展一個變種通用接口是默認不變。您需要根據需要指定In或Out。

最後,我的解釋將是遠遠不夠的嘗試Eric Lippert's blog