Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing Properties of IEnumerator<T>.Current

Tags:

c#

ienumerable

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).

like image 619
Jens Avatar asked Jul 10 '12 10:07

Jens


People also ask

Is IEnumerator an interface?

IEnumerator is the base interface for all non-generic enumerators. Its generic equivalent is the System.

What does an IEnumerator do?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.

Is IEnumerator a method?

If we want to implement enumerator logic in any collection class, it needs to implement IEnumerable interface (either generic or non-generic). IEnumerable has just one method whereas IEnumerator has two methods (MoveNext and Reset) and a property Current.


1 Answers

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.

like image 168
Jon Avatar answered Oct 11 '22 02:10

Jon