来自 IEnumerable 的项目在 foreach 内部发生了更改,但更改未保存到集合中

本文关键字:保存 集合 IEnumerable 项目 foreach 内部 来自 发生了 | 更新日期: 2023-09-27 17:50:33

我今天有一个有趣的问题,这是一个复制品:

class TestListAndEnumerable
{
    public static void Test()
    {
        bool b1 = GetBool1(); // returns false
        bool b2 = GetBool2(); // returns true
    }
    private static bool GetBool1()
    {
        IEnumerable<BoolContainer> list = new List<bool> { false }.Select(x => new BoolContainer { Value = x });
        foreach (BoolContainer item in list)
        {
            item.Value = true;
        }
        return list.Select(x => x.Value).First();
    }
    private static bool GetBool2()
    {
        List<BoolContainer> list = new List<bool> { false }.Select(x => new BoolContainer { Value = x }).ToList();
        foreach (BoolContainer item in list)
        {
            item.Value = true;
        }
        return list.Select(x => x.Value).First();
    }
    private class BoolContainer
    {
        public bool Value { get; set; }
    }
}

GetBool1()中,更改item的值对list变量没有影响。
GetBool2()按预期工作。

有人知道为什么会这样吗?

[关于重复标签]

我认为这基本上与这个问题中的问题相同,但这里的问题更简洁明了,所以我就不说了。

来自 IEnumerable 的项目在 foreach 内部发生了更改,但更改未保存到集合中

这是 LINQ 的惰性计算问题。每次在 GetBool1() 中迭代list时,您都会创建一个新的值序列。更改其中一个对象中的 Value 属性不会更改任何其他内容。

将其与GetBool2()进行比较,其中您可以调用ToList(),这意味着您正在创建一个包含一系列对象的List<T>。现在,每次遍历该列表时,您都会获得对相同对象的引用......因此,如果您修改其中一个对象中的值,您将在下次看到该值。

如果您将Select argument更改为

x =

> { Console.WriteLine("Selecting!"(; 返回新的布尔容器 { 值 = x } };

。然后添加适当的额外Console.WriteLine调用,以便您可以看到它何时发生,您将看到在调用 ToList() 之后从未GetBool2()调用委托,但GetBool1()每次迭代序列时都会调用它。