为什么使用 foreach 时不执行此 LINQ 查询?

问题描述 投票:0回答:2

在 LINQ 语句中创建新对象时,例如:

var list = new List<string>() { "a", "b", "c" };
var created = from i in list select new A();

A 类看起来像这样:

class A
{
    public string Label;
}

然后使用 foreach 循环修改 A 中的属性:

foreach (var c in created) {
    c.Label = "Set";
}

为什么之后访问

IEnumerable
中的对象时没有设置值。例如。以下断言失败:

Assert.AreEqual("Set", created.ElementAt(2).Label);

我想知道为什么会发生这种情况。我希望 foreach 语句执行查询,并触发对象的创建。 MSDN 文档指出:“查询的执行被推迟,直到在 foreach 或 For Each 循环中迭代查询变量”

我已使用 .NET 4.5 和 Mono 3.2.0 重现了此行为。在访问创建的对象之前调用

ToList
上的
IEnumerable
可以解决这个问题。

c# .net linq foreach deferred-execution
2个回答
6
投票

发生这种情况是因为

created
是查询,而不是结果。因此,每次枚举它时,您都会从头开始重新评估
Select

如果您希望它起作用,请使

created
成为一个实际列表,而不仅仅是代表查询的
IEnumerable

例如添加:

created = created.ToList();

你说:

我希望 foreach 语句执行查询,并触发对象的创建

这正是正在发生的事情。 问题是每次迭代

created
时都会创建对象,而不仅仅是第一次。 由于
ElementAt()
方法会迭代
created
,因此它只是再次创建新的
A


0
投票

很多年后

据我了解,停留在一个循环中,对链接到foreach的Linq的查询。 我遇到过代码没有执行的情况,在调试中它被暂停了:

For Each Item In dtDatos.Rows
    Dim Str = String.Join(",", (From Dl In Ndtt Where Dl.Field(Of Integer)("MRP") = Item("MRP") And Dl.Field(Of Integer)("ProductID") =  Item("ProductID") Select Dl.Field(Of Integer )("OrdenPid")).Distinct.ToList)
    Item("Folio") = Str
Next

我通过将查询的比较值作为变量而不是作为 forach 项的一部分来解决这个问题,然后执行它。

Dim A As Integer
Dim B As Integer
For Each Item In dtDatos.Rows
    A = Item("MRP")
    B = Item("ProductID")
    Item("Folio") = String.Join(",", (From Dl In Ndtt Where Dl.Field(Of Integer)("MRP") = A And Dl.Field(Of Integer)("ProductID") = B Select Dl.Field(Of Integer)("OrdenPid")).Distinct.ToList)
Next
© www.soinside.com 2019 - 2024. All rights reserved.