c# Linq `List.AddRange` 方法不起作用

发布于 2024-09-10 04:20:14 字数 838 浏览 7 评论 0原文

我有一个定义如下的接口:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

停滞 2024-09-17 04:20:14

由于通用协方差,这在 C# 4 中有效。与以前版本的 C# 不同,存在从 IEnumerableIEnumerable 的转换。

该功能从 v2 开始就存在于 CLR 中,但仅在 C# 4 中公开(并且框架类型在 .NET 4 之前也没有利用它)。它适用于泛型接口和委托(不适用于类),并且仅适用于引用类型(因此没有从 IEnumerableIEnumerable< 的转换例如 /code>。)它也只在有意义的地方起作用 - IEnumerable 是协变的,因为对象只从 API“出来”,而 IList code> 是不变,因为您也可以使用该 API 添加值。

还支持通用逆变,在另一个方向上工作 - 例如,您可以从 IComparer转换为 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> to IEnumerable<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> to IEnumerable<object> for example.) It also only works where it makes sense - IEnumerable<T> is covariant as objects only come "out" of the API, whereas IList<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> to IComparer<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.

素染倾城色 2024-09-17 04:20:14

您没有做任何错误: List.AddRange 需要一个 IEnumerable。它不接受 IEnumerableIEnumerable

您的 foreach 循环有效。或者,您可以使用 Cast 来更改类型:

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

You're not doing anything wrong: List<TestInterface>.AddRange expects an IEnumerable<TestInterface>. It won't accept an IEnumerable<tblTestA> or IEnumerable<tblTestB>.

Your foreach loops work. Alternatively, you could use Cast to change the types:

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
枕梦 2024-09-17 04:20:14

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.

恏ㄋ傷疤忘ㄋ疼 2024-09-17 04:20:14

ab 的类型为 IEnumerableIEnumerable
list.AddRange 要求参数类型为 IEnumerable

a and b are of type IEnumerable<tblTestA> and IEnumerable<tblTestB>
While list.AddRange require the parameter to be of type IEnumerable<TestInterface>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文