В каком порядке С# для каждого цикла перебирает List <T>?

Мне было интересно узнать о том, что цикл foreach в С# проходит через объект System.Collections.Generic.List<T>.

Я нашел еще один вопрос по той же теме, но я не чувствую, что он отвечает на мой вопрос к моему удовлетворению.

Кто-то утверждает, что порядок не определен. Но, как и кто-то другой, порядок, в котором он проходит массив, фиксирован (от 0 до длины-1). 8.8.4 Инструкция foreach

Было также сказано, что то же самое справедливо для любых стандартных классов с порядком (например, List<T>). Я не могу найти документацию, чтобы поддержать это. Поэтому для всех, кого я знаю, это может сработать сейчас, но, возможно, в следующей версии .NET это будет другое (хотя это может быть маловероятно).

Я также смотрел документацию List(t).Enumerator без везения.

Другой связанный с этим вопрос гласит, что для Java он специально упоминается в документации:

List.iterator() возвращает итератор по элементам в этом списке в правильной последовательности ".

Я ищу что-то подобное в документации на С#.

Спасибо заранее.

Редактировать: Спасибо вам всем за все ваши ответы (удивительно, как быстро я получил так много ответов). Из всех ответов я понимаю, что List<T> всегда выполняет итерацию в порядке ее индексации. Но я все еще хотел бы видеть ясный мир документации, заявляя об этом, подобно документации Java на List.

Ответ 1

На странице справочного источника Microsoft для перечислителя List<T> прямо указано, что итерация выполняется от 0 до длины 1:

internal Enumerator(List<T> list) {
    this.list = list;
    index = 0;
    version = list._version;
    current = default(T);
}

public bool MoveNext() {

    List<T> localList = list;

    if (version == localList._version && ((uint)index < (uint)localList._size)) 
    {                                                     
        current = localList._items[index];                    
        index++;
        return true;
    }
    return MoveNextRare();
}

Надеюсь, это все еще актуально для кого-то

Ответ 2

В основном это до реализации IEnumerator, но при a List<T> он всегда будет находиться в естественном порядке списка, то есть в том же порядке, что и индекс: list[0], list[1], list[2] и др.

Я не считаю, что это явно задокументировано - по крайней мере, я не нашел такую ​​документацию, но я думаю, что вы можете считать ее гарантированной. Любое изменение этого заказа бессмысленно нарушало бы все виды кода. На самом деле, я был бы удивлен, увидев любую реализацию IList<T>, которая не подчинилась этому. По общему признанию, было бы неплохо увидеть, что это специально задокументировано...

Ответ 3

В вашей ссылке принятый ответ содержит С# Language Specification Version 3.0, page 240:

Порядок, в котором foreach проходит элементы массива, как следует: для одномерных массивов элементы пересекаются индекс индекса, начиная с индекса 0 и заканчивается индексом Длина - 1. Для многомерные массивы, элементы пересекаются так, что индексы наибольший размер сначала, затем следующее левое измерение, и так далее влево. Следующие пример выводит каждое значение в двумерный массив, в элементе порядок:

using System;
class Test
{
  static void Main() {
      double[,] values = {
          {1.2, 2.3, 3.4, 4.5},
          {5.6, 6.7, 7.8, 8.9}
      };
      foreach (double elementValue in values)
          Console.Write("{0} ", elementValue);
      Console.WriteLine();
  }
}

Полученный результат выглядит следующим образом: 1,2 2,3 3,4 4,5 5,6 6,7 7,8 8,9 В примере

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers) Console.WriteLine(n);
the type of n is inferred to be int, the element type of numbers.

Ответ 4

Порядок определяется итератором, используемым для перемещения коллекции данных с использованием цикла foreach.

Если вы используете стандартную коллекцию, которая является индексируемой (например, List), то она будет пересекать коллекцию, начиная с индекса 0 и перемещаясь вверх.

Если вам нужно управлять заказом, вы можете либо контролировать, как обрабатывается итерация коллекции, реализующая ваш собственный IEnumerable, или вы может сортировать список так, как вы хотите, перед выполнением цикла foreach.

Это объясняет, как Enumerator работает для общего списка. Сначала текущий элемент undefined и использует MoveNext для перехода к следующему элементу.

Если вы читаете MoveNext, это означает, что он начнется с первого элемента коллекции и оттуда перейдет к следующему, пока не будет доходит до конца коллекции.

Ответ 5

Списки, похоже, возвращают элементы в том порядке, в котором они находятся в хранилище резервных копий, поэтому, если они будут добавлены в список таким образом, они будут возвращены таким образом.

Если ваша программа зависит от заказа, вы можете отсортировать ее перед просмотром списка.

Это несколько глупо для линейных поисков, но если вам нужен заказ определенным образом, то ваш лучший выбор сделает вещи в этом порядке.

Ответ 6

Мне просто нужно было сделать что-то похожее, как быстрый взлом кода, хотя он не работал для того, что я пытался сделать, это изменило порядок для меня.

Использование LINQ для изменения порядка

         DataGridViewColumn[] gridColumns = new DataGridViewColumn[dataGridView1.Columns.Count];
         dataGridView1.Columns.CopyTo(gridColumns, 0); //This created a list of columns

         gridColumns = (from n in gridColumns
                        orderby n.DisplayIndex descending
                        select n).ToArray(); //This then changed the order based on the displayindex