Ответ 1
Я рекомендую вам прочитать раздел 8.8.4 спецификации С#, который подробно изложит ваш вопрос. Цитата из этого здесь для вашего удобства:
A для любой формулировки формы
foreach (V v in x) embedded-statement
затем расширяется до:
{
E e = ((C)(x)).GetEnumerator();
try
{
V v;
while (e.MoveNext())
{
v = (V)(T)e.Current;
embedded-statement
}
}
finally
{
code to Dispose e if necessary
}
}
Типы E, C, V и T являются перечисляющими, коллекционными, циклическими переменными и типами собираемых элементов, выведенными семантическим анализатором; Подробности см. в спецификации.
Итак, вы идете. "Foreach" - это более удобный способ записи цикла while, который вызывает MoveNext, пока MoveNext не вернет false.
Несколько тонких вещей:
-
Это не должен быть код, который сгенерирован; все, что требуется, это то, что мы генерируем код, который дает тот же результат. Например, если вы "заходите" по массиву или строке, мы просто генерируем цикл "for" (или циклы, в случае массивов multi-d), который индексирует массив или символы строки, а не принимает за счет выделения перечислителя.
-
Если перечислитель имеет тип значения, тогда код удаления может или не может выбрать коробку для перечислителя перед ее удалением. Не полагайтесь на это так или иначе. (См. http://blogs.msdn.com/b/ericlippert/archive/2011/03/14/to-box-or-not-to-box-that-is-the-question.aspx для соответствующей проблемы.)
-
Аналогичным образом, если автоматически добавленные выше приведения будут определены как конверсии идентичности, то приведение может быть отменено, даже если это обычно приводит к копированию типа значения.
-
Будущие версии С#, вероятно, помещают объявление переменной цикла
v
внутри тела цикла while; это предотвратит общую ошибку "измененного закрытия", о которой сообщается примерно раз в день о переполнении стека. [ Обновление: Это изменение действительно реализовано на С# 5.]