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);
CodePudding user response:
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.