В якому порядку C # для кожного циклу повторюється над List <T>?


85

Мені цікаво було про порядок, по якому цикл foreach у C # проходить через System.Collections.Generic.List<T>об'єкт.

Я знайшов ще одне запитання на ту саму тему, але не відчуваю, що воно відповідає на моє запитання на моє задоволення.

Хтось заявляє, що жоден порядок не визначений. Але, як заявляє хтось інший, порядок, по якому він проходить масив, є фіксованим (від 0 до Length-1). 8.8.4 Заява про прогноз

Також було сказано, що те саме справедливо для будь-яких стандартних класів із порядком (наприклад List<T>). Я не можу знайти жодної документації, що підтверджує це. Отже, наскільки я знаю, зараз це може працювати так, але, можливо, у наступній версії .NET це буде іншим способом (хоча це може бути малоймовірним).

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

Інше пов'язане питання говорить, що для Java це конкретно згадується в документації:

List.iterator()повертає ітератор над елементами у цьому списку у належній послідовності. "

Я шукаю щось подібне в документації C #.

Заздалегідь спасибі.

Редагувати: Дякую вам за всі ваші відповіді (дивно, як швидко я отримав стільки відповідей). З усіх відповідей я розумію, що List<T>завжди повторюється в порядку індексації. Але я все одно хотів би бачити чіткий спокій документації про це, подібно до документації Java наList .

Відповіді:


15

На сторінці посилання Microsoft Source для List<T>Enumerator прямо зазначено, що ітерація виконується від 0 до Length-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();
}

Сподіваюся, це все ще актуально для когось


102

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

Я не вірю, що це чітко задокументовано - принаймні, я не знайшов такої документації - але я думаю, що ви можете поводитися з нею як із гарантованим. Будь-яка зміна цього впорядкування безглуздо порушує всі види коду. Насправді, я був би здивований, побачивши будь-яку реалізацію, IList<T>яка не підкорилася цьому. Слід визнати, що було б непогано бачити це спеціально задокументовано ...


Я буквально щойно перевірив відповідь 5 секунд тому і збирався розмістити щось подібне. Ти занадто швидкий!
Michael G

Дякую за відповіді. Чи існує якась документація, яка це гарантує?
Matthijs Wessels

1
@Michael G: Я б не покладався на приклад коду MSDN як документацію. Я бачив у ньому занадто багато жахливих помилок.
Джон Скіт,

1
хто-небудь може надати ту саму відповідь щодо масиву?
yazanpro

12
@yazanpro: foreachнеодмінно повторить порядок за допомогою масиву.
Джон Скіт,

8

У вашому посиланні прийнята відповідь зазначає в C # Мовна специфікація версії 3.0, сторінка 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.

2
Так, але це для масиву. Це також автоматично застосовується для класу List <T>?
Matthijs Wessels

2
List <T> використовує масив для свого резервного сховища. Так що так.
Джоел Мюллер,

9
Але це деталь реалізації. List <T> не потрібно використовувати масив як його резервне сховище.
Ерік Ліпперт,

3
Я хотів би вказати документацію на List <T> (до прикладу коду): "Клас List <(Of <(T>)>) є загальним еквівалентом класу ArrayList. Він реалізує IList <(Of <(T>)>) загальний інтерфейс, що використовує масив , розмір якого динамічно збільшується за потреби. " (курсив мій)
RCIX,

Хм, я думаю, тоді він завжди використовує масив. Але все ж, чи означає це, що він не може повернути зворотний перелічувач?
Matthijs Wessels

4

Порядок визначається ітератором, який використовується для обходу колекції даних за допомогою циклу foreach.

Якщо ви використовуєте стандартну колекцію, що підлягає індексуванню (наприклад, Список), тоді вона пройде колекцію, починаючи з індексу 0 і рухаючись вгору.

Якщо вам потрібно контролювати порядок, ви можете або керувати тим, як обробляється ітерація колекції, реалізуючи власний IEnumerable , або ви можете відсортувати список так, як ви хочете, перед виконанням циклу foreach.

Це пояснює, як Enumerator працює для загального списку. Спочатку поточний елемент не визначений і використовує MoveNext для переходу до наступного елемента.

Якщо ви читаєте MoveNext, це означає, що він почнеться з першого елемента колекції, а звідти перейде до наступного, поки не дійде до кінця колекції.


Дякую за відповідь та доповнення (Усі відповідають так швидко, що я ледве встигаю). Я теж це читав. Можливо, я просто <слово, щоб вказати когось, хто хоче бути надто точним>, але я відчуваю, що коли вони кажуть «перший елемент», вони мають на увазі перший елемент, який буде повторено, а не елемент, який є першим згідно за порядком повторного класу.
Matthijs Wessels

Ну, якщо так важливо, щоб ви були впевнені, що все йде так, як ви очікуєте, найкращий спосіб - зробити те, що я сказав, і впровадити IEnumerable самостійно.
Brendan Enrick

1

Схоже, списки повертають товари в порядку, в якому вони знаходяться в резервному магазині, тож, якщо вони будуть додані до списку таким чином, вони повернуться таким чином.

Якщо ваша програма залежить від замовлення, ви можете відсортувати її перед обходом списку.

Для лінійних пошуків це дещо безглуздо, але якщо вам потрібне замовлення певним чином, найкраще зробити речі в такому порядку.


1

Мені просто довелося зробити щось подібне, як швидкий злом коду, хоча це не спрацювало для того, що я намагався зробити, це змінило порядок списку для мене.

Використання 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

Я не розумію, як це стосується питання. Ваш код навіть не використовує List. Я думаю, ви неправильно зрозуміли, що я мав на увазі під "списком".
Matthijs Wessels

Крім того, чому ви копіюєте вміст Columnsдо gridColumnsfirst? Я думаю, що ви могли б просто зробити:DataGridViewColumn[] gridColumns = dataGridView1.Columns.OrderByDescending(n => n.DisplayIndex).ToArray();
Маттіс Вессель

@MatthijsWessels Не складно було б змінити це, щоб використовувати List, якщо хочете.
Остін Хенлі,

@AustinHenley Ви могли б зробити прогноз на IOrderedEnumerable, але не в цьому питання. Ви також можете зробити ToList замість ToArray, але тоді у вас просто знову є Список, і питання, чи буде foreach потім циклічно обертати елементи в зазначеному порядку, все ще залишається без відповіді.
Matthijs Wessels
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.