We recently had a bug due to the behaviour of the Enumerator
of a SortedDictionary.ValueCollection
behaving differently to other enumerators. I've managed to narrow the problem down to the following (nonsensical) example:
public void Example()
{
var sorted = new SortedDictionary<string, int>
{
{"1", 1 },
{"3", 3 },
{"0", 0 },
{"2", 2 },
{"4", 4 }
};
var fromValues = sorted.Values.GetEnumerator();
fromValues.MoveNext();
var fromLinq = sorted.Select(x => x.Value).GetEnumerator();
fromLinq.MoveNext();
var fromDictionary = new Dictionary<string, int>(sorted).Values.GetEnumerator();
fromDictionary.MoveNext();
for (var i = 0; i < 3; i++)
{
Console.WriteLine($"Printing for {i}");
Print(" From Values: ", fromValues, i);
Console.WriteLine(" ------------");
Print(" From Linq: ", fromLinq, i);
Console.WriteLine(" ------------");
Print(" From Dictionary: ", fromDictionary, i);
Console.WriteLine();
}
}
private void Print(string prefix, IEnumerator<int> enumerator, int value)
{
do
{
Console.WriteLine(prefix + "Value in loop: " + enumerator.Current);
if (enumerator.Current == value)
{
Console.WriteLine(prefix + "Selected Value: " + enumerator.Current);
break;
}
} while (enumerator.MoveNext());
}
This will produce the following output:
Printing for 0
From Values: Value in loop: 0
From Values: Selected Value: 0
------------
From Linq: Value in loop: 0
From Linq: Selected Value: 0
------------
From Dictionary: Value in loop: 0
From Dictionary: Selected Value: 0
Printing for 1
From Values: Value in loop: 0
From Values: Value in loop: 1
From Values: Selected Value: 1
------------
From Linq: Value in loop: 0
From Linq: Value in loop: 1
From Linq: Selected Value: 1
------------
From Dictionary: Value in loop: 0
From Dictionary: Value in loop: 1
From Dictionary: Selected Value: 1
Printing for 2
From Values: Value in loop: 0
From Values: Value in loop: 2
From Values: Selected Value: 2
------------
From Linq: Value in loop: 1
From Linq: Value in loop: 2
From Linq: Selected Value: 2
------------
From Dictionary: Value in loop: 0
From Dictionary: Value in loop: 1
From Dictionary: Value in loop: 2
From Dictionary: Selected Value: 2
As you can see, the three Iterators
behave differently:
- SortedDictionary:
Current
is always 0 when passed to a function, butMoveNext
pushes to the correct value in the loop. - Linq: Behaves as I expected an
Iterator
to behave. - Dictionary: Resets every time the
Enumerator
is passed into a function.
I suspect the fact that SortedDictionary.ValueCollection.Enumerator
is a struct and the one produced by linq is a reference type has something to do with it. But that doesn't explain why it doesn't behave as the Enumerator
from Dictionary.