Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

Работа цикла foreach, С#


Как работает foreach, если я кладу в него не просто коллекцию, а метод, который возвращает коллекцию. Метод не будет выполняться на каждой итерации?
    


Ответы

Ответ 1



Нет, вызов Foo будет сделан только один раз. Цикл foreach (var i in Foo()) { // тело цикла } внутри заменяется на вот что: IEnumerable x = Foo(); using (var enumerator = x.GetEnumerator()) { while (enumerator.MoveNext()) { var i = enumerator.Current; // тело цикла } } Т. е., как мы видим, метод вызывается однократно. (Подробное и более точное объяснение в деталях.) Конструкция foreach -- это синтаксический сахар. При компиляции эта конструкция: foreach (var i in x) { // тело цикла } Заменяется примерно на следующий код: using (var enumerator = x.GetEnumerator()) { while (enumerator.MoveNext()) { var i = enumerator.Current; // тело цикла } } При этом x -- это экземпляр некоторого объекта, который содержит метод GetEnumerator() или имплементирует интерфейс IEnumerable (или IEnumerable), и может быть задан как в виде переменной, так и в виде выражения. Если в цикле тип переменной отличается от T: IEnumerable x = ...; foreach (SomeType i in x) { // тело цикла } То будет добавлено приведение к этому типу. При неудаче будет выброшено исключение: using (var enumerator = x.GetEnumerator()) { while (enumerator.MoveNext()) { var i = (SomeType)enumerator.Current; // тело цикла } } (Но в цикле вы не можете изменять переменную цикла i.) Конструкция foreach работает также и для коллекций, имплементирующих необобщенны IEnumerable. При этом код получается несколько другой, поскольку у необобщенного IEnumerator отсутствует метод Dispose(): var enumerator = x.GetEnumerator(); while (enumerator.MoveNext()) { var i = enumerator.Current; // тело цикла } Если у переменной цикла явно указан тип (foreach (SomeType i in Foo())), то точно так же добавляется приведение типов: var i = (SomeType)enumerator.Current; При этом тип i будет SomeType, без указания типа — object. Дополнительно советую почитать о том, как работают итераторы и IEnumerable/IEnumerable. (Например, в спецификации языка, раздел 8.8.4.) *При этом возвращаемым типом этого метода должен быть объект, имеющий открытые свойств Current и метод с сигнатурой bool MoveNext(). Это то, что называют «утиная типизация»: можно не имплементировать интерфейс IEnumerable или IEnumerable, а просто предоставить соответствующие методы.

Комментариев нет:

Отправить комментарий