Это одно из наиболее часто неправильно понятых поведений циклов в C #.
Вот что вам нужно знать:
Расчет границ цикла, если
непостоянный и с участием переменной,
доступ к свойству, вызов функции или вызов делегата
будет пересчитывать значение границ перед каждым
итерация цикла.
Так, например:
for( int i = 0; i < 1234*1234; i++ ) { ... }
В этом случае выражение 1234*1234
является постоянной времени компиляции, и в результате не будет пересчитываться на каждой итерации. Фактически он вычисляется во время компиляции и заменяется константой.
Однако в этом случае:
int k = 10;
for( int i = 0; i < k; i++ ) { k -= 1; ... }
Значение k
необходимо проверять на каждой итерации. Ведь это может измениться .. в этом примере делает. К счастью, поскольку k
является просто локальной переменной, стоимость доступа к ней очень низка - и во многих случаях она будет либо сохраняться в локальном кэше ЦП, либо, возможно, даже поддерживаться в регистре (в зависимости от того, как JIT обрабатывает и выдает машинный код).
В случае чего-то вроде следующего:
IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) { ... }
Стоимость вычисления sequence.Count()
может быть довольно дорогой. А поскольку он вычисляется на каждой итерации цикла, он может быстро сложиться.
Компилятор не может оптимизировать вызовы методов или свойств, которые встречаются в выражении границ цикла, потому что они также могут меняться с каждой итерацией . Представьте, что цикл выше был записан как:
IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) {
sequence = sequence.Concat( anotherItem );
}
Очевидно, sequence
меняется на каждой итерации ... и, следовательно, Count()
, вероятно, будет отличаться на каждой итерации. Компилятор не пытается выполнить некоторый статический анализ, чтобы определить, может ли выражение границ цикла быть константой, что было бы чрезвычайно сложно, если не невозможно. Вместо этого предполагается, что если выражение не является константой, оно должно вычисляться на каждой итерации.
Теперь, в большинстве случаев, стоимость вычисления ограничения границ для цикла будет относительно недорогой, поэтому вам не нужно об этом беспокоиться. Но вам нужно понять, как компилятор обрабатывает границы цикла следующим образом. Кроме того, будучи разработчиком , вы должны быть осторожны с использованием свойств или методов, которые имеют побочные эффекты как часть выражения границ - в конце концов, эти побочные эффекты будут возникать на каждой итерации цикла.