IEnumerable中的项目在foreach中更改,但更改未保存到集合中

时间:2015-05-23 13:44:21

标签: c# ienumerable

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

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基本上是同一个问题,但这里的问题更简洁,更重要,所以我会留下它。

1 个答案:

答案 0 :(得分:12)

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

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

如果您将List<T>更改为

x =&gt; {Console.WriteLine(&#34;选择!&#34;);返回新的BoolContainer {Value = x}};

...然后添加适当的额外Select argument来电,这样您就可以看到它发生的时间,您会看到在通话结束后{@ 1}}中的代表永远不会被调用到Console.WriteLine,但在GetBool2()中,每次迭代序列时都会调用它。