2010-07-14 50 views
6

我有一個接口定義如下:C#中的LINQ`名單<Interface> .AddRange`方法不工作

public interface TestInterface{ 
    int id { get; set; } 
} 

和兩個實現該接口的LINQ到SQL類:

public class tblTestA : TestInterface{ 
    public int id { get; set; } 
} 

public class tblTestB : TestInterface{ 
    public int id { get; set; } 
} 

我有IEnumerable列表a和b由來自tblTestA和tblTestB的數據庫記錄填充

IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable(); 
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable(); 

Howev呃,以下是不允許的:

List<TestInterface> list = new List<TestInterface>(); 
list.AddRange(a); 
list.AddRange(b); 

我必須做如下:

foreach(tblTestA item in a) 
    list.Add(item) 

foreach(tblTestB item in b) 
    list.Add(item) 

有什麼我做錯了嗎?感謝您的幫助

回答

8

這個工作在C#4,因到通用協方差。與以前版本的C#不同,有一個從IEnumerable<tblTestA>IEnumerable<TestInterface>的轉換。

該功能從v2開始就在CLR中,但它只在C#4中暴露出來(並且在.NET 4之前框架類型沒有利用它)。它僅適用於適用於通用接口和委託(不是類),僅適用於參考類型(因此,例如,沒有從IEnumerable<int>IEnumerable<object>的轉換)。它也只適用於有意義的地方 - IEnumerable<T>是協變的,因爲對象只是「出來」 「,而IList<T>不變的,因爲您也可以使用該API添加值。

通用反轉也支持,在另一個方向工作 - 例如,你可以從IComparer<object>轉換爲IComparer<string>

如果你不使用C#4,那麼Tim的使用Enumerable.Cast<T>的建議是一個很好的建議 - 你失去了一點效率,但它會工作。

如果您想了解更多關於通用差異的信息,Eric Lippert有一個long series of blog posts about it,我在NDC 2010上對此進行了討論,您可以在NDC video page上觀看。

6

你沒有做錯任何事情:List<TestInterface>.AddRange預計IEnumerable<TestInterface>。它不會接受IEnumerable<tblTestA>IEnumerable<tblTestB>

您的foreach循環工作。另外,您也可以使用Cast改變類型:

List<TestInterface> list = new List<TestInterface>(); 
list.AddRange(a.Cast<TestInterface>()); 
list.AddRange(b.Cast<TestInterface>()); 
+0

+1感謝您的修復,非常感謝 – Jimbo 2010-07-14 10:34:23

0

abIEnumerable<tblTestA>類型和IEnumerable<tblTestB>
雖然list.AddRange需要的參數是類型的IEnumerable<TestInterface>

+0

那麼,他們需要參數* convertible *爲'IEnumerable < TestInterface>'。無論大小寫取決於你使用的C#版本...... – 2010-07-14 09:55:21

+0

我猜你是對的 - 我不熟悉C#4,謝謝:) – 2010-07-14 09:59:20

1

AddRange期望接口對象的列表,並且您的「a」和「b」變量被定義爲派生類對象的列表。顯然,這似乎是合理的。NET可以邏輯地進行跳轉,並將它們視爲接口對象的列表,因爲它們確實實現了接口,該邏輯僅僅在3.5以前不能構建到.NET中。但是,這種能力(稱爲「協方差」)已經被添加到.NET 4.0中,但是直到你升級到這種能力之後,你會停滯在循環中,或者嘗試調用ToArray(),然後將結果轉換爲一個TaskInterface []或LINQ查詢來處理每個項目並創建一個新列表等。