С# foreach loop - это порядок * стабильность * гарантирован?

Предположим, что у меня есть данный набор. Не меняя коллекцию каким-либо образом, я дважды повторяю ее содержимое с помощью foreach. Запрещая космические лучи, а что нет, абсолютно ли гарантировано, что порядок будет согласован в обеих петлях?

В качестве альтернативы, учитывая HashSet<string> с несколькими элементами, что может привести к неравномерности вывода из комментариев строк ниже:

{
    var mySet = new HashSet<string>();
    // Some code which populates the HashSet<string>

    // Output1
    printContents(mySet);

    // Output2
    printContents(mySet);
}

public void printContents(HashSet<string> set) {
    foreach(var element in set) {
         Console.WriteLine(element);
    }
}

Было бы полезно, если бы я мог получить общий ответ, объясняющий, что приводит к тому, что реализация не отвечает критериям, описанным выше. В частности, меня интересуют Dictionary, List и массивы.

Ответ 1

Перечисление массива гарантирует порядок.

Ожидается, что

List и List<T> будут обеспечивать стабильный порядок (поскольку ожидается, что они будут реализовывать последовательно индексированные элементы).

Словарь, HashSet явно не гарантирует порядок. Очень маловероятно, что 2 обращения к итерации элементов один за другим возвратят элементы в другом порядке, но нет никаких гарантий или ожиданий. Нельзя ожидать какого-либо конкретного порядка.

Отсортированные версии словаря /HashSet возвращают элементы в порядке сортировки.

Другие объекты IEnumerable могут делать все, что захотят. Обычно один реализует итераторы таким образом, чтобы соответствовать ожиданиям пользователей. То есть перечисление чего-либо, имеющего неявный порядок, должно быть стабильным, если предусматривается явный порядок - ожидается, что он будет стабильным. Ожидается, что запрос к базе данных, который не указывает порядок, должен возвращать элементы в полуслучайном порядке.

Проверить этот вопрос для ссылок: Предоставляет ли цикл foreach в С# порядок оценки?

Ответ 2

Все, что реализует IEnumerable<T>, делает это по-своему. Нет никакой общей гарантии, что любой сбор должен обеспечить стабильность.

Если вы конкретно ссылаетесь на Collection<T> (http://msdn.microsoft.com/en-us/library/ms132397.aspx), я не вижу никакой конкретной гарантии в своей ссылке MSDN, что заказ согласован.

Возможно, он будет последовательным? Да. Есть ли письменная гарантия? Не то, что я могу найти.

Ответ 3

Для многих коллекций С# существуют отсортированные версии коллекции. Например, HashSet относится к SortedSet как Dictionary относится к SortedDictionary, Если вы работаете с чем-то, где порядок не важен, например, Dictionary, вы не можете предположить, что порядок цикла будет вести себя одинаково каждый раз.

Ответ 4

В соответствии с вашим примером с HashSet<T> теперь у нас есть исходный код для проверки: HashSet: Enumerator

Как бы то ни было, массив Slot[] set.m_slots повторяется. Объект массива изменяется только в методах TrimExcess, Initialize (оба из которых вызываются только в конструкторе), OnDeserialization и SetCapacity (вызываются только AddIfNotPresent и AddOrGetLocation).

Значения m_slots изменяются только в методах, которые меняют элементы HashSet (Clear, Remove, AddIfNotPresent, IntersectWith, SymmetricExceptWith).

Итак, да, если ничто не касается набора, оно перечисляется в том же порядке.

Словарь: Перечислитель работает совершенно таким же образом, итерация Entry[] entries, которая изменяется только при вызове таких нечитанных методов.