c# Linq `List.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)
是不是我哪里做错了?感谢您的帮助
I have an interface defined as below:
public interface TestInterface{
int id { get; set; }
}
And two Linq-to-SQL classes implementing that interface:
public class tblTestA : TestInterface{
public int id { get; set; }
}
public class tblTestB : TestInterface{
public int id { get; set; }
}
I have IEnumerable lists a and b populated by the database records from tblTestA and tblTestB
IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable();
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable();
However, the following is not permitted:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a);
list.AddRange(b);
I have to do as follows:
foreach(tblTestA item in a)
list.Add(item)
foreach(tblTestB item in b)
list.Add(item)
Is there something I am doing wrong? Thanks for any help
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
由于通用协方差,这在 C# 4 中有效。与以前版本的 C# 不同,存在从
IEnumerable
到IEnumerable
的转换。该功能从 v2 开始就存在于 CLR 中,但仅在 C# 4 中公开(并且框架类型在 .NET 4 之前也没有利用它)。它仅适用于泛型接口和委托(不适用于类),并且仅适用于引用类型(因此没有从
IEnumerable
到IEnumerable
还支持通用逆变,在另一个方向上工作 - 例如,您可以从
IComparer
如果您不使用 C# 4,那么 Tim 建议使用
Enumerable.Cast
是一个不错的选择 - 您会损失一点效率,但它会起作用。如果您想了解有关通用方差的更多信息,Eric Lippert 有一个关于它的一系列博客文章,我在 NDC 2010 上做了一个关于它的演讲,您可以在 NDC 视频页面。
This works in C# 4, due to generic covariance. Unlike previous versions of C#, there is a conversion from
IEnumerable<tblTestA>
toIEnumerable<TestInterface>
.The functionality has been in the CLR from v2, but it's only been exposed in C# 4 (and the framework types didn't take advantage of it before .NET 4 either). It only applies to generic interfaces and delegates (not classes) and only for reference types (so there's no conversion from
IEnumerable<int>
toIEnumerable<object>
for example.) It also only works where it makes sense -IEnumerable<T>
is covariant as objects only come "out" of the API, whereasIList<T>
is invariant because you can add values with that API too.Generic contravariance is also supported, working in the other direction - so for example you can convert from
IComparer<object>
toIComparer<string>
.If you're not using C# 4, then Tim's suggestion of using
Enumerable.Cast<T>
is a good one - you lose a little efficiency, but it will work.If you want to learn more about generic variance, Eric Lippert has a long series of blog posts about it, and I gave a talk about it at NDC 2010 which you can watch on the NDC video page.
您没有做任何错误:
List.AddRange
需要一个IEnumerable
。它不接受IEnumerable
或IEnumerable
。您的
foreach
循环有效。或者,您可以使用Cast
来更改类型:You're not doing anything wrong:
List<TestInterface>.AddRange
expects anIEnumerable<TestInterface>
. It won't accept anIEnumerable<tblTestA>
orIEnumerable<tblTestB>
.Your
foreach
loops work. Alternatively, you could useCast
to change the types:AddRange 需要一个接口对象列表,并且“a”和“b”变量被定义为派生类对象的列表。显然,.NET 可以在逻辑上进行跳转并将它们视为接口对象列表,这似乎是合理的,因为它们确实实现了接口,只是在 3.5 之前的 .NET 中没有构建该逻辑。
然而,这种能力(称为“协方差”)已添加到 .NET 4.0 中,但在升级到该能力之前,您将陷入循环困境,或者尝试调用 ToArray(),然后将结果转换为 TaskInterface[] ,或者可能是一个 LINQ 查询来区分每个项目并创建一个新列表等。
The AddRange is expecting a list of interface objects, and your "a" and "b" varaibles are defined to be a list of derived class objects. Obviously, it seems reasonable that .NET could logically make that jump and treat them as lists of the interface objects because they do indeed implement the interface, that logic was just not build into .NET up through 3.5.
However, this ability (called "covariance") has been added to .NET 4.0, but until you upgrade to that, you'll be stuck with looping, or maybe try calling ToArray() and then casting the result to a TaskInterface[], or maybe a LINQ query to case each item and create a new list, etc.
a
和b
的类型为IEnumerable
和IEnumerable
而
list.AddRange
要求参数类型为IEnumerable
a
andb
are of typeIEnumerable<tblTestA>
andIEnumerable<tblTestB>
While
list.AddRange
require the parameter to be of typeIEnumerable<TestInterface>