LINQ 在循环并修改使用 .Where() 过滤的可枚举项中的项目后,项目消失 - 为什么?

问题描述

在修复一些代码错误时,我遇到了一些意想不到的行为,似乎项目在循环后从 IEnumerable 中消失了。

我试图理解为什么会发生这种情况,我编写了一个小型测试项目来测试这种行为。

简单解释一下下面的代码

  1. 我有一个类 (TestObject),它只有一个属性 - 一个 int
  2. 我创建了一个 TestObject 列表,并向其中添加了 5 个新的 TestObject,所有这些都具有 int 属性 == 5
  3. 我使用 LINQ 的 .Where() 函数“过滤”列表,只获取 int == 5 的 TestObjects
    (列表中的每个元素)
  4. 我遍历 IEnumerable 的元素,将每个元素的 int 属性设置为 0
  5. 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() 的背景中完成的 yield return 有关,它获取可枚举中与谓词匹配的下一个元素,我通过改变来干扰它迭代时可枚举的元素,但有人可以确认/解释这一点吗?

(我确实尝试在 this link 处查找有关 .Where() 函数行为的任何文档,但它并没有真正回答我的问题。)

这也是.AsEnumerable() 似乎没有这个问题的原因吗?

感谢您抽出宝贵时间。

解决方法

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