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

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

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

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

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


.net loops c#
person Joan Venge    schedule 17.03.2009    source источник


Ответы (5)


arrow_upward
30
arrow_downward

Да Количество будет оцениваться при каждом проходе. Причина в том, что коллекция может быть изменена во время выполнения цикла. Учитывая структуру цикла, переменная 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

arrow_upward
14
arrow_downward

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

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

arrow_upward
4
arrow_downward

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

person Kelsey    schedule 17.03.2009

arrow_upward
4
arrow_downward

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

Существует (по крайней мере) одно заметное исключение, 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

arrow_upward
3
arrow_downward

Примечание: это НЕ проверяется для каждого взаимодействия в 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