IEnumerable中的项目已在foreach中更改,但更改未保存到集合中 [英] Item from IEnumerable changed inside foreach, but change not saved to collection
问题描述
我今天有一个有趣的问题,这是复制品:
I had an interesting problem today, this is a reproduction:
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()
可以正常工作.
Inside GetBool1()
, changing the value of item
has no effect on the list
variable.
GetBool2()
works as expected.
有人知道为什么会这样吗?
Anyone know why this is happening?
[关于重复标签]
我认为这与此问题基本上是相同的问题,但是这里的问题更加简洁,点,所以我就离开它.
I think it IS fundamentally the same problem as in this question, but the question here is more concise and to the point so I'll leave it.
推荐答案
这是LINQ的懒惰评估问题.每次在GetBool1()
中的list
上进行迭代时,都会创建一个新的值序列.更改其中一个对象的Value
属性不会更改其他任何内容.
This is a matter of LINQ's lazy evaluation. Each time you iterate over list
in GetBool1()
, you're creating a new sequence of values. Changing the Value
property in one of those objects doesn't change anything else.
将其与GetBool2()
进行比较,在那里您可以调用ToList()
,这意味着您将创建一个包含一系列对象的List<T>
.现在,每次迭代该列表时,您都会将会获得对相同对象的引用...因此,如果您在这些对象之一中修改值,则下次会看到该值.
Compare that with GetBool2()
, where you're got a call to ToList()
, which means you're creating a List<T>
with a sequence of objects in. Now each time you iterate over that list, you'll get references to the same objects... so if you modify the value within one of those objects, you'll see that next time.
如果将Select argument
更改为
x => {Console.WriteLine("Selecting!");返回新的BoolContainer {Value = x}};
x => { Console.WriteLine("Selecting!"); return new BoolContainer { Value = x } };
...然后添加适当的额外Console.WriteLine
调用,这样您就可以看到调用发生的时间,您会看到在调用ToList()
之后,从未在GetBool2()
中调用委托,而在GetBool1()
中每次您迭代序列时都会调用它.
... then add appropriate extra Console.WriteLine
calls so you can see when it's taking place, you'll see that the delegate is never called in GetBool2()
after the call to ToList()
, but in GetBool1()
it will be called each time you iterate over the sequence.
这篇关于IEnumerable中的项目已在foreach中更改,但更改未保存到集合中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!