Ответ 1
(обновление)
На самом деле - существует один сценарий, в котором конструкция for
более эффективна; цикл по массиву. У компилятора /JIT есть оптимизация для этого сценария, если вы используете arr.Length
в условии:
for(int i = 0 ; i < arr.Length ; i++) {
Console.WriteLine(arr[i]); // skips bounds check
}
В этом конкретном случае он пропускает проверку границ, поскольку он уже знает, что он никогда не будет за пределами границ. Интересно, что если вы "подталкиваете" arr.Length
, чтобы попытаться оптимизировать его вручную, вы не позволили этому произойти:
int len = arr.Length;
for(int i = 0 ; i < len ; i++) {
Console.WriteLine(arr[i]); // performs bounds check
}
Однако, с другими контейнерами (List<T>
и т.д.), подъем довольно разумен как ручная микро-оптимизация.
(окончательное обновление)
Ни; цикл for в любом случае оценивается как цикл while под капотом.
Например, 12.3.3.9 из ECMA 334 (определенное задание) диктует, что цикл for:
for ( for-initializer ; for-condition ; for-iterator ) embedded-statement
по существу эквивалентен (с точки зрения Определенное назначение (не совсем то же самое, что сказать "компилятор должен сгенерировать этот IL" )):
{
for-initializer ;
while ( for-condition ) {
embedded-statement ;
LLoop:
for-iterator ;
}
}
с инструкциями continue, которые нацелены перевод на инструкции goto, нацеленные на ярлык Lloop. Если условие условия опущено из утверждения for, тогда оценка определенного задания поступает так, как будто условие состояния заменен на true в приведенном выше расширения.
Теперь это не означает, что компилятор должен делать то же самое, но на самом деле это в значительной степени делает...