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

Item from IEnumerable changed inside foreach, but change not saved to collection

我今天遇到了一个有趣的问题,这是一个复现:

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() 按预期工作。

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

[关于重复标签]

我认为它与 this question 中的问题基本相同,但这里的问题更加简洁和切题,所以我将保留它。

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

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

如果您将 Select argument 更改为

x => { Console.WriteLine("Selecting!"); return 新的 BoolContainer { 值 = x } };

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