C#Linq'列表<Interface>.AddRange'方法不工作
题
我有一个定义如下的接口:
public interface TestInterface{
int id { get; set; }
}
和两个实现该接口的Linq-to-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();
但是,以下是不允许的:
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)
有什么我做错了吗?谢谢你的帮助
解决方案
这在C#4中工作,由于 泛型协方差.与以前版本的C#不同,有一个从 IEnumerable<tblTestA>
到 IEnumerable<TestInterface>
.
该功能已经在V2的CLR中,但它只在C#4中公开(<url>4之前,框架类型也没有利用它)。它 只有 适用于泛型接口和委托(不是类),仅适用于引用类型(因此没有从 IEnumerable<int>
到 IEnumerable<object>
例如。)它也只在有意义的地方工作 - IEnumerable<T>
是协变的,因为对象只从API中"出来",而 IList<T>
是 不变性 因为您也可以使用该API添加值。
也支持通用逆变,在另一个方向工作-例如,您可以从 IComparer<object>
到 IComparer<string>
.
如果你没有使用C#4,那么Tim的使用建议 Enumerable.Cast<T>
是一个很好的-你失去了一点效率,但它会工作。
如果你想了解更多关于泛型方差的信息,Eric Lippert有一个 关于它的长系列博客文章, ,我在NDC2010上做了一个演讲,你可以在 NDC视频页面.
其他提示
你没做错什么: 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>());
AddRange正在期待接口对象列表,您的“a”和“b”varaibles被定义为派生类对象列表。显然,它似乎是合理的.NET可以在逻辑上使其跳转并将它们视为接口对象的列表,因为它们确实实现了接口,该逻辑仅在3.5上才会构建。
但是,这种能力(称为“协议”)已被添加到.NET 4.0,但直到您升级到那个时,您将被困在循环中,或者可能会尝试调用ToArray(),然后将结果转换为taskInterface [],或者可能是一个linq查询,以便案例每个项目,并创建一个新列表等。a
和 b
类型 IEnumerable<tblTestA>
和 IEnumerable<tblTestB>
而 list.AddRange
要求参数为类型 IEnumerable<TestInterface>