C# Linq的List<Interface>.AddRange方法无法正常工作

7
我定义了一个如下的接口:
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)

我有做错的地方吗?感谢任何帮助。
4个回答

9
这在C# 4中有效,因为具有通用协变性。与C#的先前版本不同,从IEnumerable到IEnumerable存在一种转换。
该功能已经在CLR v2中实现,但仅在C# 4中公开(框架类型也没有在.NET 4之前利用它)。它仅适用于通用接口和委托(而不是类),仅适用于引用类型(例如,从IEnumerable到IEnumerable没有转换)。它仅在有意义的地方起作用——IEnumerable作为对象只从API“输出”,而IList是不变的,因为您也可以使用该API添加值。
通用逆变性也得到支持,在另一个方向上工作——例如,您可以将IComparer转换为IComparer。
如果您没有使用C# 4,那么Tim建议使用Enumerable.Cast<T>是一个不错的选择——您会失去一些效率,但它可以工作。
如果您想了解更多关于泛型变异的内容,Eric Lippert有一系列长篇博客文章,而我在NDC 2010上也做了一个关于此主题的演讲,您可以在NDC视频页面上观看。

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>());

1
AddRange需要一个接口对象列表,而你的"a"和"b"变量被定义为派生类对象列表。显然,.NET可以逻辑上使它们成为接口对象列表,因为它们确实实现了这个接口,但是直到3.5版本,这种逻辑没有被内置到.NET中。
然而,这种能力(称为"协变性")已经添加到.NET 4.0中,但是在升级到该版本之前,你将被困在循环中,或者尝试调用ToArray(),然后将结果转换为TaskInterface[],或者使用一个LINQ查询来处理每个条目并创建一个新列表,等等。

0

ab的类型分别为IEnumerable<tblTestA>IEnumerable<tblTestB>
list.AddRange需要参数的类型为IEnumerable<TestInterface>


好的,它们要求参数可以转换为 IEnumerable<TestInterface>。这是否成立取决于您使用的 C# 版本... - Jon Skeet
我想你是对的 - 我不熟悉C#4,谢谢 :) - Itay Karo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接