Why Enumerator MoveNext() returns 0

253 Views Asked by At

Why next code generates loop hangs?

var x = new
{
    Items = new List<int> { 1, 2, 3 }.GetEnumerator()
};

while (x.Items.MoveNext())
   Console.WriteLine(x.Items.Current);

If I get enumerator after x initialization, than everything works fine:

var x = new
{
    Items = new List<int> { 1, 2, 3 }
};

var enumerator = x.Items.GetEnumerator();

while (enumerator.MoveNext())
   Console.WriteLine(enumerator.Current);

Tried to decompile both code blocks, but don't understand the reason:

List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);
intList.Add(3);
var data1 = new \u003C\u003Ef__AnonymousType0<List<int>.Enumerator>(intList.GetEnumerator());
while (true)
{
   List<int>.Enumerator items = data1.Items;
   if (items.MoveNext())
   {
     items = data1.Items;
     Console.WriteLine(items.Current);
   }
   else
     break;
}
List<int> Items = new List<int>();
Items.Add(1);
Items.Add(2);
Items.Add(3);
var data2 = new \u003C\u003Ef__AnonymousType0<List<int>>(Items);
List<int>.Enumerator enumerator = data2.Items.GetEnumerator();
while (enumerator.MoveNext())
   Console.WriteLine(enumerator.Current);
1

There are 1 best solutions below

0
CookedCthulhu On BEST ANSWER

Because List's enumerator is a struct. In your first example, you're creating an enumerator object (somewhere, probably heap, doesn't matter) and every time you access it with .Items the enumerator gets copied onto the stack in your executing function. MoveNext mutates it and then the copied enumerator gets discarded. If it were a class, only the reference would be copied/discarded and the actual data modified but since it's a struct, you didn't modify the enumerator in var x { ... } at all. Enumerator.Current returns 0 because default(int) is zero but an exception would be possible, too.

In your second example you're creating a slot for the enumerator on the stack with var enumerator = x.Items.GetEnumerator(); and that slot gets accessed repeadedly without copies.