2012-07-10 128 views
10

怀着极大的惊讶,我今天观察到以下行为:给定一个类改变属性.Current

class Foo 
{ 
    prop int FooNumber { get; set; } 
} 

和验证码

IEnumerable<Foo> foos = Enumerable.Range(0,3).Select(new Foo()); 

foreach (var foo in foos) 
    foo.Bar = 5; 

foreach (var foo in foos) 
    Console.Write(foo.Bar); // Writes 000 

同时初始化foosnew List<Foo>{ new Foo(), new Foo(), new Foo() }使循环写入“ 555" 。

我的问题:为什么会发生这种情况,并且有没有办法使用.ToList()(需要注释,因为在这里似乎不需要)来绕过这个问题。

+4

欢迎来到ReSharper称之为“可能的多重枚举”的奇妙世界。一个枚举是**不是**集合。事实上,有时一个枚举可以覆盖集合,并且您可以在下面修改它,这是一个副作用。 – 2012-07-10 10:20:14

回答

20

发生这种情况是因为foos是每次枚举它时动态生成的。因此,在第一次迭代期间,您将在迭代结束后为不再由任何东西引用的对象设置属性值。第二次迭代对具有默认属性值的新构建的对象起作用。

初始化foos为“持久性”的对象的列表变化的东西,如使用.ToList()出于同样的原因做(“固定的”列表被构造和遍历两次;将原始动态产生IEnumerable仅遍历一次)。

已经确定,你应该在这里使用.ToList():一般我不觉得这需要评论,因为它不是习惯迭代不止一次动态生成的序列(相信很多代码分析工具警惕这),但通过一切手段写一个。

+0

解释它,谢谢。我的错误印象是,第一次枚举后,结果将存储在某个地方。 – Jens 2012-07-10 10:25:32

3

看起来很明显发生了什么:每次你枚举时,你都会实例化新的Foo对象。

如果你想要的属性值(Foo.Bar)被保留了,那么你将不得不保持Foo的某处和ToList()是这样做的一个简单的方法。