Проверяется ли условие в цикле for на каждой итерации?

Когда вы делаете такие вещи, как:

for (int i = 0; i < collection.Count; ++i )

вызывается collection.Count на каждой итерации?

Изменится ли результат, если свойство Count будет динамически получать количество вызовов?


person Joan Venge    schedule 17.03.2009    source источник


Ответы (5)


Да Количество будет оцениваться при каждом проходе. Причина в том, что коллекция может быть изменена во время выполнения цикла. Учитывая структуру цикла, переменная i должна представлять действительный индекс в коллекции во время итерации. Если проверка не выполнялась в каждом цикле, то это недоказуемо. Пример случая

for ( int i = 0; i < collection.Count; i++ ) {
  collection.Clear();
}

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

for ( int i = 0; i < someArray.Length; i++ ) {
  // Code
}

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

Ссылка: http://blogs.msdn.com/brada/archive/2005/04/23/411321.aspx

person JaredPar    schedule 17.03.2009
comment
Что, если я изменю размер массива внутри цикла? - person Joan Venge; 17.03.2009
comment
@ Хенк, да, это часть эвристики, которая определяет, могут ли они использовать этот цикл в особом случае. - person JaredPar; 17.03.2009
comment
@Jared: Но у Array есть статический метод Resize, верно? Это то, что я имел в виду, хотя, вероятно, назначение нового массива. Кроме того, когда вы сказали, что это часть эвристики, вы имеете в виду, что компилятор тоже это распознает? - person Joan Venge; 17.03.2009
comment
@JaredparJaredpar, я начинаю сомневаться - не путаем ли мы это с оптимизацией проверки границ? Свойство Length преобразуется в код операции ldlen, я бы назвал это встраиванием. - person Henk Holterman; 17.03.2009
comment
@ Джоан, да, изменение размера есть, но оно создает новый массив. Обратите внимание, что первый параметр берется по ссылке. - person JaredPar; 17.03.2009
comment
@ Хенк, и то, и другое понемногу. JIT выполнит здесь только одну проверку привязки в оптимизированном сценарии. В сценарии без массива он будет выполнять N связанных проверок, которые требуют оценки N свойств. - person JaredPar; 17.03.2009
comment
@Jared: Вы знаете, почему это занял рефери? Я не знаю, почему это так определено? - person Joan Venge; 17.03.2009
comment
@ Джоан, это потому, что размер массива вообще нельзя изменить. Он берет его по ссылке, потому что делает копию массива и возвращает копию обратно. Таким образом, он появляется только для изменения размера массива. - person JaredPar; 17.03.2009
comment
Имеет ли string.Length такое же поведение в цикле for? - person Aran Mulholland; 28.01.2016
comment
Другая причина заключается в том, что вы можете делать что-то еще, кроме перебора коллекций. Например. for (int p = capacity; !IsPrime(p); p++) { } гарантирует, что p будет простым. - person Olivier Jacot-Descombes; 04.07.2018
comment
Причина в том, что коллекция может быть изменена во время выполнения цикла, что является преимуществом повторной оценки каждого цикла, но я думаю, что Причина (почему должно быть именно таким) заключается в том, что i < collection.Count — это просто логическое значение, и цикл будет повторяться до тех пор, пока не станет false. Если вы войдете в цикл с true и больше никогда не проверите, цикл никогда не выйдет. - person Raphael Schmitz; 19.07.2018

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

class Program
    {
        static void Main(string[] args)
        {
            List<int> intCollection = new List<int>();
            for(int i=-1;i < intCollection.Count;i++)
            {
                intCollection.Add(i + 1);
            }
        }
    }

Это в конечном итоге приведет к исключению нехватки памяти.

person Jeff Martin    schedule 17.03.2009
comment
Разве С# не мешает вам изменять коллекцию во время ее итерации? Или это происходит только для каждого цикла? - person i_am_jorf; 17.03.2009
comment
Это происходит только тогда, когда вы используете Enumerators - person dustyburwell; 17.03.2009
comment
@jeffamaphone - я думал то же самое, но написал небольшую программу, которая продемонстрировала, что это не так. Интересный. - person Jay Riggs; 17.03.2009
comment
ОП не говорит, что такое коллекция, но я использовал список‹› для своего теста. - person Jay Riggs; 17.03.2009

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

person Kelsey    schedule 17.03.2009

Как и другие ответы здесь: Да, в принципе.

Существует (по крайней мере) одно заметное исключение, array.Length. В

for (int i = 0; i < a.Length; i++) a[i] = ...;

Свойство Length будет оцениваться только один раз. Это оптимизация, встроенная в компилятор. Могут быть и другие подобные (в будущем), но только если это гарантированно не будет иметь значения в наблюдаемом поведении.

person Henk Holterman    schedule 17.03.2009
comment
Насколько я знаю, компилятор, о котором идет речь, является компилятором JIT, а не компилятором C#. - person Jon Skeet; 17.03.2009
comment
@John: Не уверен, оба компилятора знают, что массив исправлен. Глядя на IL, я бы предположил, что это компилятор C#. - person Henk Holterman; 17.03.2009
comment
Что, если я изменю размер массива внутри цикла? - person Joan Venge; 17.03.2009
comment
@ Джоан ... почему бы тебе не попробовать - создание небольшого тестового приложения, подобного тому, что я показал выше, занимает около 60 секунд. Новый проект, консольное приложение... - person Jeff Martin; 17.03.2009
comment
Я могу попробовать, но я на компе друга. Можно использовать компилятор snipper, но не хочу ничего устанавливать. - person Joan Venge; 17.03.2009
comment
Чтобы быть явным, условие все еще оценивается, но параметр длины никогда не обновляется. Он по-прежнему оценивается на каждой итерации. Если бы у вас было true в качестве условия, оно все равно оценивалось бы на каждой итерации. - person Jeff Martin; 06.01.2012

Примечание: это НЕ проверяется для каждого взаимодействия в VB.

В отличие от C#, VB кэширует результат collection.Count.

РЕДАКТИРОВАТЬ:

Буквальная версия VB цикла C# for:

Dim i = 0
Do While i < collection.Count
    'code goes here
    i+=1
Loop
person Jonathan Allen    schedule 17.03.2009
comment
Так что, если вы измените коллекцию во время итерации, это сломается? - person Jon Skeet; 17.03.2009
comment
Если вы хотите изменить коллекцию во время цикла, вам нужно использовать цикл. - person Jonathan Allen; 19.03.2009