Я полагаю, что ответом на это является то, что перечислитель значения SortedDictionary
является структурой (типа System.Collections.Generic.SortedDictionary<string,int>.ValueCollection.Enumerator
).
То, что происходит, заключается в том, что структура копируется при каждой передаче в метод Print()
, таким образом, метод всегда работает с копией исходного перечислителя - и это вызывает странную работу.
Следующая программа демонстрирует это:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Example();
}
public static void Example()
{
var sorted = new SortedDictionary<string, int>
{
{"1", 1 },
{"3", 3 },
{"0", 0 },
{"2", 2 },
{"4", 4 }
};
var fromValues1 = sorted.Values.GetEnumerator();
// fromValue1 type is struct System.Collections.Generic.SortedDictionary<string,int>.ValueCollection.Enumerator
fromValues1.MoveNext();
while (printNextValue(fromValues1)) // Prints 0 once for each value in the dictionary.
;
Console.WriteLine("-----------------");
IEnumerator<int> fromValues2 = sorted.Values.GetEnumerator();
// fromValues2 type is boxed struct System.Collections.Generic.SortedDictionary<string,int>.ValueCollection.Enumerator
fromValues2.MoveNext();
while (printNextValue(fromValues2)) // Prints each value in the dictionary.
;
}
static bool printNextValue(IEnumerator<int> enumerator)
{
Console.WriteLine(enumerator.Current);
return enumerator.MoveNext();
}
}
первый l oop выводит все нули, а второй выводит правильные значения.
Единственное различие между двумя циклами в этом примере состоит в том, что первый итератор объявлен как:
var fromValues1 = sorted.Values.GetEnumerator();
А второй объявлен как:
IEnumerator<int> fromValues2 = sorted.Values.GetEnumerator();
.
В результате первого объявления fromValues1
будет структурой, а второе - в штучной упаковке struct.
Поскольку структура упакована, это означает, что она НЕ копируется при передаче в printNextValue()
, что означает, что printNextValue()
будет работать с исходным перечислителем, а не с его копией.
* 102 9 * Однако это НЕ объясняет, почему l oop завершается! Если исходная позиция перечислителя копировалась при каждом вызове
printNextValue()
, то l oop никогда не завершится, поскольку позиция исходного перечислителя никогда не будет обновлена.
Это заставляет меня поверить, что некоторые Сложность в реализации SortedDictionary.Enumerator
означает, что при копировании структуры некоторые ее данные копируются, а некоторые нет.
(Глядя на исходный код, я подозреваю, что это связано с реализация перечислителя Current
является типом значения, которое копируется, но MoveNext()
, по-видимому, манипулирует стеком, который - будучи ссылочным типом - разделяется между всеми копиями перечислителя. Однако код слишком сложен для меня, чтобы проанализировать в мое в настоящее время ограниченное время ...)