LINQ 在使用 .Where() 循环和修改可枚举过滤器中的项目后,项目消失了 - 为啥? [复制]
Posted
技术标签:
【中文标题】LINQ 在使用 .Where() 循环和修改可枚举过滤器中的项目后,项目消失了 - 为啥? [复制]【英文标题】:LINQ after looping over and modifying items in enumerable filtered with .Where(), items disappear - why? [duplicate]LINQ 在使用 .Where() 循环和修改可枚举过滤器中的项目后,项目消失了 - 为什么? [复制] 【发布时间】:2021-05-20 19:54:39 【问题描述】:在修复一些代码的错误时,我遇到了一些意想不到的行为,在循环之后,项目似乎从 IEnumerable 中消失了。
我试图了解为什么会发生这种情况,并编写了一个小型测试项目来测试这种行为。
下面的代码简单解释一下,
-
我有一个类 (TestObject),它只有一个属性 - 一个 int
我创建了一个 TestObject 列表并向其中添加 5 个新的 TestObject,所有这些都具有 int 属性 == 5
我使用 LINQ 的 .Where() 函数“过滤”列表,只获取 int == 5 的 TestObjects
(列表中的每个元素)
我循环遍历 IEnumerable 的元素,将每个元素的 int 属性设置为 0
IEnumerable 现在为空
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestingGroundProject
class TestObject
public TestObject(int intVal)
IntVal = intVal;
public int IntVal get; set;
class Program
static void Main(string[] args)
List<TestObject> testList = new();
for (int i = 0; i < 5; i++)
testList.Add(new TestObject(5));
//this line results in expected behavior
//(5 elements in testEnum, all IntVal's are 0 after the loop)
//var testEnum = testList.AsEnumerable();
//this line results in unexpected behavior
//(0 elements in testEnum after the loop)
var testEnum = testList.Where(t => t.IntVal == 5);
//prints "5"
Console.WriteLine(testEnum.Count());
foreach (var t in testEnum)
t.IntVal = 0;
//prints "0"
Console.WriteLine(testEnum.Count());
我猜这与在 .Where() 内部的后台执行的收益返回有关,该收益返回获取与谓词匹配的可枚举中的下一个元素,我通过更改来干扰它迭代时可枚举的元素,但有人可以确认/解释这一点吗?
(我确实尝试在this link 查找有关 .Where() 函数行为的任何文档,但它并没有真正回答我的问题。)
这也是 .AsEnumerable() 似乎没有这个问题的原因吗?
感谢您的宝贵时间。
【问题讨论】:
这是因为 LINQ 在你调用Count
之前是懒惰的。它仅在此调用后才开始过滤。因此,您已经更改了元素并过滤了省略的先前值。如果你想要稳定,只需调用 ToList() - var testEnum = testList.Where(t => t.IntVal == 5).ToList();
如果你调用 Where 唯一要做的就是返回一个实现 IEnumerable 的新类,一旦枚举了枚举,它将返回满足给定谓词的项目。此行为记录在备注部分的链接中。
@SvyatoslavDanyliv 为了我的清楚,你是说第二个Count
正在第二次过滤列表吗?
是的,只有ToList
、ToArray
等会立即运行过滤器并将结果存储在收集中。
别担心,接受第一个答案,它是正确的;)
【参考方案1】:
IEnumerable 在你调用需要迭代的东西之前不会执行,比如你的 Count()。因此,您调用 Count() 的两次序列将分别计算。这是物品第二次消失的原因。如果您希望将序列保持在给定点,您可以调用 ToList() 并将其保存在变量中。
【讨论】:
以上是关于LINQ 在使用 .Where() 循环和修改可枚举过滤器中的项目后,项目消失了 - 为啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 LINQ 修改 IEnumerable 以避免 foreach 循环? [复制]