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 =&gt; t.IntVal == 5).ToList(); 如果你调用 Where 唯一要做的就是返回一个实现 IEnumerable 的新类,一旦枚举了枚举,它将返回满足给定谓词的项目。此行为记录在备注部分的链接中。 @SvyatoslavDanyliv 为了我的清楚,你是说第二个Count 正在第二次过滤列表吗? 是的,只有ToListToArray等会立即运行过滤器并将结果存储在收集中。 别担心,接受第一个答案,它是正确的;) 【参考方案1】:

IEnumerable 在你调用需要迭代的东西之前不会执行,比如你的 Count()。因此,您调用 Count() 的两次序列将分别计算。这是物品第二次消失的原因。如果您希望将序列保持在给定点,您可以调用 ToList() 并将其保存在变量中。

【讨论】:

以上是关于LINQ 在使用 .Where() 循环和修改可枚举过滤器中的项目后,项目消失了 - 为啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 LINQ 修改 IEnumerable 以避免 foreach 循环? [复制]

Linq:Select 和 Where 有啥区别

Linq 中的 Groupby 和 where 子句

LINQ 查询在 C# 中使用实体框架获取单列和单行值而不使用 Where

扩展方法和Enumerable

实体框架 Linq 查询:.Where 链 vs &&