2010-07-14 30 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查询来处理每个项目并创建一个新列表等。