В .NET какой цикл выполняется быстрее, «для» или «foreach»?

345

В C # / VB.NET / .NET какой цикл работает быстрее forили foreach?

С тех пор, как я прочитал, что forцикл работает быстрее, чем foreachцикл давным-давно, я предположил, что это верно для всех коллекций, универсальных коллекций, всех массивов и т. Д.

Я просмотрел Google и нашел несколько статей, но большинство из них неубедительны (читай комментарии к статьям) и открыты.

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

Например (просто пример того, как это должно быть):

  1. для перебора массива из 1000+ строк - forлучше, чемforeach
  2. для перебора IList(не универсальных) строк - foreachлучше, чемfor

Несколько ссылок, найденных в Интернете для того же:

  1. Оригинальная старая статья Эммануэля Шанцера
  2. CodeProject FOREACH Vs. ДЛЯ
  3. Блог - к foreachили нет foreach, вот в чем вопрос
  4. Форум ASP.NET - NET 1.1 C # forпротивforeach

[Редактировать]

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

Binoj Antony
источник
3
Разница все еще существует. В частности, массивы должны быть такими же быстрыми в foreach, но для всего остального простые циклы быстрее. Конечно, в большинстве случаев это не будет иметь значения, и, конечно, умный JIT-компилятор может теоретически устранить разницу.
Джалф
3
Без контекста я не могу точно знать, что вы делаете, но что происходит, когда вы сталкиваетесь с частично заполненным массивом?
Крис Кадмор
6
Кстати, 2 миллиона показов в месяц - ничего страшного. Это в среднем меньше, чем удар в секунду.
Мехрдад Афшари
37
Важное примечание : вчера этот вопрос был объединен с совершенно не связанным с этим вопросом о том, чтобы его заставляли использовать foreachвместо forC #. Если вы видите здесь ответы, которые вообще не имеют смысла, вот почему. Виноват модератор, а не несчастные ответы.
TED
7
@TED ​​О, мне было интересно, откуда все комментарии "твой босс - идиот", откуда они пришли, спасибо
Gaspa79

Ответы:

351

Об этом в прошлом месяце написал Патрик Смаккья со следующими выводами:

  • Циклы for в List чуть более чем в 2 раза дешевле, чем циклы foreach в List.
  • Зацикливание в массиве примерно в 2 раза дешевле, чем зацикливание в List.
  • Как следствие, зацикливание на использовании массива for в 5 раз дешевле, чем зацикливание на List с использованием foreach (что, я считаю, является тем, что мы все делаем).
Ян Нельсон
источник
130
Однако никогда не забывайте: «Преждевременная оптимизация - корень всего зла».
Ооранг
18
@Hardwareguy: Если вы знаете, что for почти незаметно быстрее, почему бы вам вообще не начать его использовать? Это не займет дополнительное время.
DevinB
48
@devinb, использовать «for» сложнее, чем «foreach», так как он добавляет код, другую переменную, условие, которое нужно проверить, и т. д. Сколько раз вы видели ошибку «один за другим» в цикле «foreach» ?
tster
35
@ Hardwareguy, дай мне посмотреть, правильно ли я понял. Для циклического перемещения по списку требуется в 5 раз больше времени, foreachчем для обхода массива for, и вы называете это незначительным? Такая разница в производительности может иметь значение для вашего приложения, а может и нет, но я бы просто не отмахнулся от нее.
Роберт Харви
44
Прочитав сообщение в блоге, похоже, что тесты были запущены в Debug, а не в Release, так что это может иметь значение. Кроме того, разница специально для заголовка цикла. Это никак не влияет на время выполнения тела цикла, которое в большинстве случаев намного дольше, чем время, необходимое для перехода к следующему элементу списка. Полезно знать, когда вы определили, что существует определенная проблема, и вы конкретно измерили разницу в своем приложении, и есть заметное улучшение, но определенно нет общего совета по удалению всех foreachs.
Davy8
165

Сначала встречный иск к ответу Дмитрия (теперь удален) . Для массивов компилятор C # выдает в основном тот же код, foreachчто и для эквивалентного forцикла. Это объясняет, почему для этого теста результаты в основном такие же:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

Полученные результаты:

For loop: 16638
Foreach loop: 16529

Далее, проверка того, что Грег говорит о важности типа коллекции - измените массив на a List<double>в приведенном выше, и вы получите радикально разные результаты. Это не только значительно медленнее в целом, но foreach становится значительно медленнее, чем доступ по индексу. Сказав это, я все равно почти всегда предпочитаю foreach циклу for, где он упрощает код - потому что читаемость почти всегда важна, а микрооптимизация редко.

Джон Скит
источник
«Измените массив на List <double> в приведенном выше, и вы получите совершенно другие результаты» Очень интересно, я не думал об этом
johnc
5
Учитывая странные различия в результатах между моими тестами и тестами других людей, я думаю, что это заслужит сообщение в блоге ...
Джон Скит
1
Что вы почти всегда предпочитаете между массивами и List<T>? Читаемость также превосходит микрооптимизацию в этом случае?
JohnB
12
@JohnB: Да, я почти всегда предпочитаю List<T>массивы. Исключения char[]и byte[]которые чаще рассматриваются как «куски» данные , а не обычные коллекции.
Джон Скит
Удивительно, но я получаю еще более агрессивную разницу на моей машине, почти на 10% в пользу foreach на простых массивах. Я дико догадываюсь, что все это происходит из-за дрожания, не нужно беспокоиться о дополнительных переменных, связанных проверках и т. Д. Было бы очень интересно, если бы у кого-то было глубокое объяснение этого.
Тамир Даниели
162

foreachциклы демонстрируют более конкретное намерение, чем forциклы .

Использование foreachцикла демонстрирует любому, кто использует ваш код, что вы планируете что-то сделать для каждого члена коллекции независимо от его места в коллекции. Это также показывает, что вы не изменяете исходную коллекцию (и выдает исключение, если вы пытаетесь это сделать).

Другое преимущество foreachзаключается в том, что он работает в любом месте IEnumerable, где forимеет смысл только то IList, где каждый элемент имеет индекс.

Однако, если вам нужно использовать индекс элемента, тогда, конечно, вам должно быть разрешено использовать forцикл. Но если вам не нужно использовать индекс, его наличие просто загромождает ваш код.

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

ctford
источник
23
ctford: нет, это не так. Компилятор, конечно, не может переупорядочивать элементы в foreach. foreachнисколько не связано с функциональным программированием. Это абсолютно обязательная парадигма программирования. Вы неправильно приписываете вещи, происходящие в TPL и PLINQ foreach.
Мехрдад Афшари
13
@BlueTrin: Это, безусловно, гарантирует порядок (раздел 8.8.4 спецификации C # формально определяет foreachкак эквивалент whileцикла). Я думаю, я знаю, что @ctford имеет в виду. Задача параллельной библиотеки позволяет базовой коллекции предоставлять элементы в произвольном порядке (путем вызова .AsParallelперечислимого). foreachздесь ничего не делается, и тело цикла выполняется в одном потоке . Единственное, что распараллелено, - это генерация последовательности.
Мехрдад Афшари
6
Enumerable.Select имеет перегрузку, позволяющую получить индекс элемента, поэтому даже необходимость в индексе не требует использования for. См. Msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill,
5
ForEach удобен для удобства чтения и сохранения набора текста. Затраты имеют значение, хотя; изменение 2 или 3 циклов for в пользовательском интерфейсе дизайнера документов, которое я сделал, с (для каждого объекта в списке) на (для i = 0 до list.count-1) уменьшило время отклика с 2-3 секунд на редактирование до примерно. 5 секунд на редактирование небольшого документа, просто повторяя несколько сотен объектов. Теперь даже для огромных документов нет увеличения времени, чтобы все зациклить. Я понятия не имею, как это произошло. Что я знаю, так это то, что альтернативой была сложная схема, состоящая в том, чтобы зацикливать только подмножество объектов. Я возьму 5-минутную смену в любой день! - не микрооптимизация.
FastAl
5
@FastAl Разница между foreachи forпроизводительностью для обычных списков составляет доли секунды для итерации по миллионам элементов, поэтому ваша проблема, безусловно, не была напрямую связана с производительностью foreach, по крайней мере, с несколькими сотнями объектов. Походит на сломанную реализацию перечислителя в любом списке, который вы использовали.
Майк Мариновски,
53

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

Используйте класс StopWatch и повторите что-то несколько миллионов раз для точности. (Это может быть трудно без цикла for):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

Пальцы пересекли результаты этого шоу, что разница незначительна, и вы могли бы просто сделать любые результаты в наиболее поддерживаемом коде

Роб Фонсека-Энсор
источник
13
Но вы не можете сравнить производительность для и foreach. Они должны быть использованы в разных обстоятельствах.
Майкл Крелин - хакер
7
Я согласен с вами, Майкл, вы не должны выбирать, какой из них использовать в зависимости от производительности - вы должны выбрать тот, который имеет больше смысла! Но если ваш босс говорит: «Не используйте для, потому что это медленнее, чем foreach», тогда это единственный способ убедить его в том, что разница незначительна
Роб Фонсека-Энсор
«(Это может быть трудно без цикла for)» Или вы можете использовать цикл while.
Jonescb
49

Это всегда будет близко. Для массива иногда for немного быстрее, но foreachон более выразителен и предлагает LINQ и т. Д. В общем, придерживайтесьforeach .

Кроме того, foreachможет быть оптимизирован в некоторых сценариях. Например, связанный список может быть ужасным для индексатора, но он может быть быстрым foreach. На самом деле, стандарт LinkedList<T>даже не предлагает индексатор по этой причине.

Марк Гравелл
источник
Так ты говоришь, LinkedList<T>что стройнее, чем List<T>? И если я всегда буду использовать foreach(вместо for), мне лучше использовать LinkedList<T>?
JohnB
2
@JohnB - не стройнее; просто другой. Например, каждый узел в связанном списке имеет дополнительные ссылки, которые не нужны для плоского массива (который также лежит в основе List<T>). Больше того, что дешевле вставить / удалить .
Марк Гравелл
36

Я предполагаю, что это, вероятно, не будет значимым в 99% случаев, так почему бы вам выбрать более быстрый, а не наиболее подходящий (как это проще всего понять / сохранить)?

Брайан Расмуссен
источник
6
@klew, если вы на самом деле профилируете свой код, вам не придется угадывать, какие 20% должны быть максимально быстрыми Вы, вероятно, также узнаете, что фактическое количество циклов, которые должны быть быстрыми, намного меньше. Кроме того, вы действительно говорите, что зацикливание - это то, где вы проводите свое время, а не то, что вы фактически делаете в этом цикле?
tster
32

Вряд ли между ними будет огромная разница в производительности. Как всегда, когда сталкиваешься с "что быстрее?" Вопрос, вы всегда должны думать: «Я могу измерить это».

Напишите два цикла, которые делают одно и то же в теле цикла, выполните и рассчитайте их оба, и посмотрите, какова разница в скорости. Делайте это как с почти пустым телом, так и с телом цикла, похожим на то, что вы на самом деле будете делать. Также попробуйте это с типом коллекции, который вы используете, потому что разные типы коллекций могут иметь разные характеристики производительности.

Грег Хьюгилл
источник
32

Есть очень веские причины, чтобы предпочесть foreach петли над forпетлями. Если вы можете использовать foreachпетлю, ваш босс прав, что вы должны.

Однако не каждая итерация просто просматривает список по порядку. Если он запрещает , да, это неправильно.

На вашем месте я бы превратил все ваши естественные циклы в рекурсию . Это научит его, и это также хорошее умственное упражнение для вас.

ТЕД
источник
Как рекурсия сравнивается с forциклами и foreachциклами по производительности?
JohnB
Это зависит. Если вы используете хвостовую рекурсию и ваш компилятор достаточно умен, чтобы заметить, он может быть идентичным. OTOH: Если это не так, и вы делаете что-то глупое, например, передаете много несекретных (неизменяемых) данных в качестве параметров или объявляете большие структуры в стеке локальными, это может быть очень медленным (или даже исчерпать ОЗУ).
TED
Ааа. Я понимаю, почему вы спросили это сейчас. Этот ответ пошел на совершенно другой вопрос. По какой-то странной причине Джонатан Сэмпсон вчера объединил их. Он действительно не должен был этого делать. Объединенные ответы не будут иметь здесь никакого смысла.
TED
18

Джеффри Рихтер на TechEd 2005:

«Я пришел к выводу, что на протяжении многих лет компилятор C # для меня лжец». .. "Это ложь о многих вещах." .. "Например, когда вы делаете цикл foreach ..." .. "... это одна маленькая строчка кода, которую вы пишете, но то, что выдает компилятор C # для того, чтобы сделать это, это феноменально. там блок try / finally, внутри блока finally он преобразует вашу переменную в интерфейс IDisposable, и если приведение завершается успешно, он вызывает для него метод Dispose, внутри цикла он вызывает свойство Current, а метод MoveNext несколько раз внутри цикла, объекты создаются под крышками. Многие люди используют foreach, потому что это очень легко кодировать, очень легко делать ... ".." foreach не очень хорош с точки зрения производительности,

Веб-трансляция по требованию: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US

Макс Торо
источник
12

Это смешно. Нет никаких веских причин, чтобы запретить цикл, производительность или другие.

См . Блог Джона Скита для оценки производительности и других аргументов.

Rik
источник
2
Обновленная ссылка: codeblog.jonskeet.uk/2009/01/29/…
Мэтт
Более быстрая конструкция цикла зависит от того, что вам нужно перебрать. Другой блог, который сравнивает несколько итераций с различными типами объектов , такими как DataRows и пользовательские объекты. Он также включает в себя производительность конструкции цикла While, а не только конструкции цикла for и foreach.
Бесплатный кодер 24
11

В случаях, когда вы работаете с коллекцией объектов, foreachэто лучше, но если вы увеличиваете число, forцикл лучше.

Обратите внимание, что в последнем случае вы можете сделать что-то вроде:

foreach (int i in Enumerable.Range(1, 10))...

Но он, конечно, не работает лучше, на самом деле он хуже по сравнению с for.

Meta-Knight
источник
«Лучше» является спорным: Это медленнее и dnspy отладчик не сломается в C # Еогеаспа (хотя VS2017 отладчика). Иногда удобочитаемее, но если вы поддерживаете языки без него, это может раздражать.
Zeek2
10

Это должно спасти вас:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

Использование:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

Для большего выигрыша вы можете взять трех делегатов в качестве параметров.

akuhn
источник
1
Одно крошечное отличие состоит в том, что forцикл обычно пишется для исключения конца диапазона (например 0 <= i < 10). Parallel.Forтакже делает это, чтобы держать его легко взаимозаменяемым с общим forциклом.
Groo
9

Вы можете прочитать об этом в Deep .NET - часть 1 Итерация

он охватывает результаты (без первой инициализации) из исходного кода .NET вплоть до разборки.

например - Итерация массива с циклом foreach: введите описание изображения здесь

и - список итераций с циклом foreach: введите описание изображения здесь

и конечные результаты: введите описание изображения здесь

введите описание изображения здесь

Или яков
источник
8

Различия в скорости в for- и foreach-loop крошечные, когда вы просматриваете общие структуры, такие как массивы, списки и т. Д., И делаетеLINQ запроса к коллекции почти всегда немного медленнее, хотя писать приятнее! Как говорили другие авторы, стремитесь к выразительности, а не к миллисекунде дополнительной производительности.

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

Другое ключевое преимущество foreachцикла состоит в том, что если ваша реализация коллекции изменится (например, с int arrayна a List<int>), то ваш foreachцикл не потребует никаких изменений кода:

foreach (int i in myCollection)

Выше приведено одно и то же, независимо от типа вашей коллекции, тогда как в вашем forцикле следующее не будет построено, если вы изменили myCollectionс a arrayна a List:

for (int i = 0; i < myCollection.Length, i++)
Алекс Йорк
источник
7

«Есть ли какие-либо аргументы, которые я мог бы использовать, чтобы убедить его, что цикл for приемлем для использования?»

Нет, если ваш начальник на микроуровне говорит о том, какой язык программирования использовать, вам нечего сказать. Сожалею.

кругозор
источник
7

Вероятно, это зависит от типа коллекции, которую вы перечисляете, и от реализации ее индексатора. В целом, использование foreach, вероятно, будет лучшим подходом.

Кроме того, он будет работать с любыми IEnumerable- не только с индексаторами.

Эндрю Кеннан
источник
7

На это есть те же два ответа, что и на большинство вопросов «что быстрее»:

1) Если вы не измеряете, вы не знаете.

2) (потому что ...) Это зависит.

Это зависит от того, насколько дорогой метод "MoveNext ()", относительно того, насколько дорогой метод "this [int index]", для типа (или типов) IEnumerable, который вы будете перебирать.

Ключевое слово «foreach» является сокращением для ряда операций - оно вызывает GetEnumerator () один раз в IEnumerable, оно вызывает MoveNext () один раз за итерацию, выполняет некоторую проверку типов и так далее. Наиболее вероятным фактором, влияющим на измерения производительности, является стоимость MoveNext (), поскольку она вызывается O (N) раз. Может быть, это дешево, но, возможно, это не так.

Ключевое слово «for» выглядит более предсказуемым, но внутри большинства циклов «for» вы найдете что-то вроде «collection [index]». Это похоже на простую операцию индексации массива, но на самом деле это вызов метода, стоимость которого полностью зависит от природы коллекции, для которой вы выполняете итерацию. Возможно, это дешево, но, возможно, это не так.

Если базовая структура коллекции по сути представляет собой связанный список, MoveNext является дешевым, но индексатор может иметь стоимость O (N), что составляет истинную стоимость цикла for (O * N).

NSFW
источник
6

Каждая языковая конструкция имеет подходящее время и место для использования. Есть причина, по которой язык C # имеет четыре отдельных итерационных оператора - каждый из них предназначен для определенной цели и имеет соответствующее применение.

Я рекомендую сесть с вашим боссом и попытаться рационально объяснить, почему у forпетли есть цель. Есть моменты, когда forитерационный блок более четко описывает алгоритм, чем foreachитерация. Когда это правда, уместно использовать их.

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

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

Рид Копси
источник
5

Именно то, что вы делаете внутри цикла, влияет на производительность, а не на фактическую конструкцию цикла (если ваш случай нетривиален).

Мартин Викман
источник
5

Будь forбыстрее, чем foreachна самом деле, кроме сути. Я серьезно сомневаюсь, что выбор одного из других окажет значительное влияние на вашу производительность.

Лучший способ оптимизировать ваше приложение - это профилирование реального кода. Это позволит точно определить методы, на которые приходится больше всего работы / времени. Оптимизируйте те сначала. Если производительность по-прежнему не приемлема, повторите процедуру.

Как правило, я бы рекомендовал держаться подальше от микрооптимизаций, поскольку они редко приносят какие-либо существенные выгоды. Единственное исключение - при оптимизации идентифицированных «горячих путей» (т. Е. Если ваше профилирование идентифицирует несколько широко используемых методов, возможно, имеет смысл оптимизировать их экстенсивно).

Брайан Расмуссен
источник
Если бы в проектах, над которыми я работаю, единственным видом оптимизации было микрооптимизация, я был бы счастливым туристом. К сожалению, это никогда не так.
Янник Моттон
2
forнезначительно быстрее, чем foreach. Я серьезно возражаю против этого заявления. Это полностью зависит от основной коллекции. Если класс связанного списка предоставляет индексатору с целочисленным параметром, я ожидал бы, что использование forцикла для него будет O (n ^ 2), в то время foreachкак ожидается, что O (n).
Мердад Афшари
@Merhdad: На самом деле это хороший момент. Я просто думал о обычном случае индексации списка (то есть массива). Я перефразирую это, чтобы отразить это. Спасибо.
Брайан Расмуссен
@ Mehrdad Afshari: индексирование коллекции по целому числу может быть намного медленнее, чем перечисление по ней. Но вы на самом деле сравниваете использование for и поиск индексатора с использованием foreachотдельно. Я думаю, что ответ @Brian Rasmussen правильный, что, помимо любого использования с коллекцией, forвсегда будет немного быстрее, чем foreach. Тем не менее, forпоиск коллекции всегда будет медленнее, чем foreachсам по себе.
Даниэль Приден
@Daniel: Либо у вас есть простой массив, для которого оба будут генерировать идентичный код, либо при использовании forоператора используется индексатор . Обычный forцикл с целочисленной переменной управления не сравним с foreach, так что нет. Я понимаю, что означает @Brian, и, как вы говорите, это правильно, но ответ может вводить в заблуждение. Re: ваш последний пункт: нет, на самом деле, forпо- List<T>прежнему быстрее, чем foreach.
Мердад Афшари
4

Два будут работать практически одинаково. Напишите некоторый код, чтобы использовать оба, затем покажите ему IL. Он должен показывать сопоставимые вычисления, что означает отсутствие различий в производительности.

Дальневосточные
источник
Компилятор распознает циклы foreach, используемые в массивах / ILists и т. Д., И изменяет их на циклы for.
Каллум Роджерс
3
Покажите ему ряды неразборчивых доказательств того, что все в порядке, и попросите доказательств того, что это не так.
cjk
3

for имеет более простую логику для реализации, поэтому она быстрее, чем foreach.

Тарик
источник
3

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

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

GeekyMonkey
источник
3

В большинстве случаев нет никакой разницы.

Как правило, вы всегда должны использовать foreach, когда у вас нет явного числового индекса, и вы всегда должны использовать его, когда у вас фактически нет итеративной коллекции (например, итерации по двумерной сетке массива в верхнем треугольнике) , В некоторых случаях у вас есть выбор.

Можно утверждать, что циклы for могут быть немного сложнее поддерживать, если в коде начинают появляться магические числа. Вы должны быть правы, если будете раздражены тем, что не можете использовать цикл for и должны собирать коллекцию или использовать лямбду для создания подколлекции вместо этого только потому, что циклы for были запрещены.

Кейд Ру
источник
3

Кажется немного странным полностью запретить использование чего-то вроде цикла for.

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

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

colethecoder
источник
Необходимая цитата из статьи, на которую вы ссылаетесь: «... если вы планируете писать высокопроизводительный код, который не предназначен для коллекций, используйте цикл. Даже для коллекций foreach может показаться удобным при использовании, но это не так эффективно».
NickFitz
3

Я нашел foreachцикл, который перебирает List быстрее . Смотрите результаты моего теста ниже. В приведенном ниже коде я выполняю итерации arrayразмером 100, 10000 и 100000 по отдельности, используя цикл forи foreachцикл для измерения времени.

введите описание изображения здесь

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

ОБНОВЛЕНО

После предложения @jgauffin я использовал код @johnskeet и обнаружил, что forцикл с arrayболее быстрым, чем следующий,

  • Цикл Foreach с массивом.
  • Для цикла со списком.
  • Цикл Foreach со списком.

Смотрите результаты моего теста и код ниже,

введите описание изображения здесь

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }
оборота Диганта Кумар
источник
3
Это очень плохой тест. а) вы делаете слишком мало итераций, чтобы получить окончательный ответ б) что Thread.Sleep действительно не будет ждать одну миллисекунду. Используйте тот же метод, что и Джон Скит в своем ответе.
jgauffin
1
99,99% времени определенно тратится в thread.sleep (что не дает гарантии того, насколько быстро он вернется, за исключением того, что раньше этого времени не было). Цикл очень быстрый, а сон очень медленный, вы не используете последний, чтобы проверить первый.
Ронан Тибо,
3

Вы можете действительно ввернуть его голову и пойти на IQueryable .foreach закрытие вместо этого:

myList.ForEach(c => Console.WriteLine(c.ToString());
Тэд Донаге
источник
3
Я бы заменил вашу строку кода на myList.ForEach(Console.WriteLine).
Мехрдад Афшари
2

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

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

Конечно, этот ответ не учитывает никаких оптимизаций, которые может реализовать компилятор.

JohannesH
источник
Компилятор C # выполняет очень мало оптимизаций, он оставляет это на усмотрение JITter.
LJS
Ну, JITter - это компилятор ... Верно?
Йоханнес
2

Имейте в виду, что цикл for и цикл foreach не всегда эквивалентны. Перечислители списка будут генерировать исключение, если список изменится, но вы не всегда получите это предупреждение с обычным циклом for. Вы можете даже получить другое исключение, если список меняется не вовремя.

Крейг Гидни
источник
Если список меняется из-за вас, вы не можете полагаться на счетчик, поддерживающий цикл foreach, чтобы сказать вам это. Он не будет проверять снова после того, как вернет вам значение, что приведет к гонке.
Худатикус