Подтвердить что ты не робот

Что происходит за сценой цикла "foreach"?

Возможный дубликат:
Как работают циклы foreach на С#?

Я искал интернет, и у меня возникли проблемы с поиском ответов на то, что действительно происходит за кулисами с циклом foreach на С#.

Я знаю, что этот вопрос действительно не относится к собственно кодированию, но он беспокоит меня. Я новичок в программировании OO и особенно в интерфейсах. Я понимаю, что это контракты, и я понимаю, как работают IEnumerable и IEnumerator - или так я думаю.

Я читал эту статью в MSDN: IEnumerable Interface

Я понимаю, как все настроено. Я немного неясен, хотя в цикле Main, как foreach знает, чтобы повторить все значения в _people. Как он это знает? Как он отслеживает Current, просто позвонив return new PeopleEnum(_people);?

EDIT: я не вижу, как это точный дубликат. Да, его аналогичные основания и тот же вопрос задают, но мы ищем разные ответы или ответ, который я хотел, не обсуждался в предыдущем вопросе.

A foreach loop, как foreach (int я в obj) {...} kinda, равен

... является "любопытным", а не тем ответом, который я ищу.

4b9b3361

Ответ 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.]

Ответ 2

Рассмотрим это следующим образом:

 var enumerator = <YourEnumerableHere>.GetEnumerator();

 while(enumerator.MoveNext())
    <YourForEachMethodBodyHere>

Ergo, когда он вызывает GetEnumerator, вы возвращаете этот результат в PeopleEnum. Каждый раз, когда вызывается метод MoveNext, позиция PeopleEnum увеличивается на единицу, итерируя по списку.

Когда позиция достигла конца, вызов MoveNext возвращает false, и цикл завершается.

Ответ 3

Он знает итерацию через IEnumerable, потому что это должны были сделать разработчики языка foreach. foreach предоставляет вам сокращенный способ прокрутки через IEnumerable самостоятельно.

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

Я не уверен, ответил ли я на ваш вопрос. Если нет, сообщите мне, и я отвечу.