Reputation: 25583
With great surprised I observed the following behavior today: Given a class
class Foo
{
prop int FooNumber { get; set; }
}
and this code
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
while initializing foos
to new List<Foo>{ new Foo(), new Foo(), new Foo() }
makes the loop write "555".
My question: Why does this happen and is there a way to circumvent this whithout using .ToList()
(which needs a comment, since it does not seem to be needed here).
Upvotes: 10
Views: 279
Reputation: 437644
It happens because foos
is dynamically produced each time you enumerate it. So during the first iteration you are setting property values on objects that are no longer referenced by anything after the iteration ends. The second iteration works on freshly constructed objects which have the default property value.
Initializing foos
to a list of "persistent" objects changes things, as does using .ToList()
for the same reason (a "fixed" list is constructed and iterated over twice; the original dynamically produced IEnumerable
is only iterated over once).
Having established that you should use .ToList()
here: in general I do not feel that it needs a comment because it is not customary to iterate over dynamically produced sequences more than once (I believe many code analysis tools warn against this), but by all means do write one.
Upvotes: 20
Reputation: 124766
It seems obvious what's happening: each time you enumerate, you're instantiating new Foo objects.
If you want the property values (Foo.Bar
) to be preserved, then you're going to have to keep the Foo's somewhere, and ToList() is a simple way of doing this.
Upvotes: 3