今天我遇到了一个有趣的问题,这是一个复制品:
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基本上是同一个问题,但这里的问题更简洁,更重要,所以我会留下它。
答案 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()
中,每次迭代序列时都会调用它。