Foreach
Оператор foreach осуществляет перечисление элементов коллекции, выполняя внедренный оператор для каждого элемента коллекции. оператор_foreach: Тип и идентификатор в каждом операторе foreach объявляют итерационную переменную оператора. Если тип_локальной_переменной задан идентификатором var, и в области видимости не существует типа именованного var, говорят, что это неявно введенная итерационная переменная, и в качестве ее типа принимается тип элементов оператора foreach, как описано ниже. Итерационная переменная соответствует локальной переменной, доступной только на чтение, область видимости которой охватывает внедренный оператор. В ходе выполнения оператора foreach итерационная переменная представляет элемент коллекции, для которого в данный момент выполняется итерация. Если внедренный оператор пытается изменить итерационную переменную (путем присваивания или с помощью операторов ++ и ‑‑) или передать ее как параметр ref или out, возникает ошибка времени компиляции. Во время компиляции оператора foreach сначала определяется тип коллекции;, тип перечислителя; и тип элементов; для выражения. Это происходит следующим образом. · Если типом X выражения является тип массива, производится неявное преобразование ссылочного типа X в интерфейс System.Collections.IEnumerable (поскольку System.Array реализует этот интерфейс). В качестве типа коллекции принимается интерфейс System.Collections.IEnumerable, в качестве типа перечислителя — интерфейс System.Collections.IEnumerator, и в качестве типа элементов — тип элементов массива типа X. · Если тип X выражения; имеет вид dynamic, существует неявное преобразование типа из выражения; в интерфейс System.Collections.IEnumerable (§6.1.8). В качестве типа коллекции принимается интерфейс System.Collections.IEnumerable, в качестве типа перечислителя — интерфейс System.Collections.IEnumerator. Если идентификатор var представлен как тип_локальной_переменной;, то типом элемента будет dynamic, в противном случае – object. · В противном случае следует определить, существует ли для типа X соответствующий метод GetEnumerator. o Выполняется поиск членов для типа X с идентификатором GetEnumerator без использования аргументов типа. Если поиск членов не дает результата, или дает неоднозначный результат, или находит член, не являющийся группой методов, следует проверить наличие перечислимого интерфейса, как описано ниже. Рекомендуется выдавать предупреждение, если результатом поиска членов является что-либо, кроме группы методов или отсутствия совпадений. o Выполняется разрешение перегрузки с использованием результирующей группы методов и пустого списка аргументов. Если разрешение перегрузки не позволяет получить применимые методы, дает неоднозначный результат или приводит к единственному наилучшему методу, который описан как статический или не открытый, следует проверить наличие перечислимого интерфейса, как описано ниже. Рекомендуется выдавать предупреждение, если результатом разрешения перегрузки является что-либо, кроме однозначно определенного открытого метода экземпляра или отсутствия применимых методов. o Если тип возвращаемого значения E метода GetEnumerator не является типом класса, структуры или интерфейса, возникает ошибка, и больше никакие действия не выполняются. o Выполняется поиск членов для типа E с идентификатором Current без использования аргументов типа. Если поиск членов не дает результата, приводит к ошибке или находит что-либо, кроме открытого свойства экземпляра, разрешающего чтение, возникает ошибка, и больше никакие действия не выполняются. o Выполняется поиск членов для типа E с идентификатором MoveNext без использования аргументов типа. Если поиск членов не дает результата, приводит к ошибке или находит что-либо, кроме группы методов, возникает ошибка, и больше никакие действия не выполняются. o Выполняется разрешение перегрузки для группы методов с использованием пустого списка аргументов. Если разрешение перегрузки не позволяет получить применимые методы, дает неоднозначный результат или приводит к единственному наилучшему методу, который описан как статический или не открытый либо возвращает значение типа, отличного от bool, возникает ошибка, и больше никакие действия не выполняются. o В качестве типа коллекции принимается X, в качестве типа перечислителя — E, в качестве типа элементов — тип свойства Current. · В противном случае проверяется наличие перечислимого интерфейса. o Если имеется ровно один тип T, для которого существует неявное преобразование типа X в интерфейс System.Collections.Generic.IEnumerable<T>, то в качестве типа коллекции принимается этот интерфейс, в качестве типа перечислителя — интерфейс System.Collections.Generic.IEnumerator<T>, и в качестве типа элементов — T. o Если таких типов T несколько, возникает ошибка, и больше никакие действия не выполняются. o Если существует неявное преобразование типа X в интерфейс System.Collections.IEnumerable, в качестве типа коллекции принимается этот интерфейс, в качестве типа перечислителя — интерфейс System.Collections.IEnumerator, и в качестве типа элементов — object. o В противном случае возникает ошибка, и больше никакие действия не выполняются. Если описанная выше процедура завершается успешно, она дает однозначно определенный результат: тип коллекции C, тип перечислителя E и тип элементов T. После этого оператор foreach, заданный в виде foreach (V v in x) внедренный_оператор развертывается следующим образом: { Переменная e невидима и недоступна для выражения x, внедренного оператора и любого другого исходного кода программы. Переменная v доступна только для чтения во внедренном операторе. Если не определено явное преобразование (§6.2) типа T (типа элементов) в V (тип_локальной_переменной; в операторе foreach), возникает ошибка, и больше никакие действия не выполняются. Если x имеет значение null, во время выполнения генерируется исключение System.NullReferenceException. Для любого оператора foreach разрешается использовать реализацию, в которой он обрабатывается иначе (например, для повышения быстродействия), при условии сохранения поведения, совместимого с вышеописанным развертыванием. Тело блока finally строится по следующим правилам. · Если существует неявное преобразование E в интерфейс System.IDisposable, то o Если E является необнуляемым типом значений, предложение finally развертывается в семантический эквивалент следующего: finally { o В противном случае предложение finally развертывается в семантический эквивалент следующего: finally { Исключение составляют ситуации, когда E является типом значения, или же параметром типа, экземпляром которого оказывается тип значения; в этих случаях приведение e к System.IDisposable не сопровождается упаковкой. · Если E является запечатанным типом, предложение finally развертывается в пустой блок: finally { · В противном случае предложение finally развертывается следующим образом: finally { Локальная переменная d невидима или недоступна для любого пользовательского кода. В частности, она не вступает в конфликты с другими переменными, чьи области видимости включают блок finally. Оператор foreach обходит элементы массива в следующем порядке. Элементы одномерных массивов обходятся в порядке возрастания индекса, начиная с индекса 0 и заканчивая индексом Length – 1. Элементы многомерных массивов обходятся сначала по возрастанию индексов самого правого измерения, затем по возрастанию индексов следующего измерения слева и так далее до самого левого измерения. В следующем примере выводится каждое значение двумерного массива в порядке следования элементов: using System; class Test foreach (double elementValue in values) Console.WriteLine(); Вывод выглядит следующим образом: 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 В этом примере int[] numbers = { 1, 3, 5, 7, 9 }; в качестве типа n неявно принимается int — тип элементов массива numbers. 8.9 Операторы перехода; Операторы перехода осуществляют безусловную передачу управления. оператор_перехода: Точка, в которую оператор перехода передает управление, называется его целью. Если оператор перехода находится внутри блока, а его цель — вне этого блока, говорят, что оператор перехода производит выход из блока. Оператор перехода может передавать управление за пределы блока, но он никогда не передает управление внутрь блока. Выполнение оператора перехода усложняется при наличии сопутствующих операторов try. В отсутствие таких операторов try оператор перехода выполняет безусловную передачу управления своей цели. При его использовании вместе с операторами try схема выполнения становится более сложной. Если оператор перехода производит выход из одного или нескольких блоков try, с которыми связаны соответствующие блоки finally, управление вначале передается в блок finally самого внутреннего оператора try. Если управление достигает конечной точки блока finally, после этого управление передается в блок finally следующего объемлющего оператора try. Этот процесс повторяется до тех пор, пока не будут выполнены все блоки finally всех сопутствующих операторов try. В этом примере using System; class Test перед передачей управления в цель оператора перехода выполняются блоки finally, соответствующие двум операторам try. Вывод выглядит следующим образом: Before break Break Оператор break осуществляет выход из ближайшего объемлющего оператора switch, while, do, for или foreach. оператор_break: Целью оператора break является конечная точка ближайшего объемлющего оператора switch, while, do, for или foreach. Если оператор break не содержится ни в каком операторе switch, while, do, for или foreach, возникает ошибка времени компиляции. Если несколько операторов switch, while, do, for или foreach вложены друг в друга, оператор break применяется только к самому внутреннему из них. Для передачи управления с переходом через несколько уровней вложенности следует использовать оператор goto (§8.9.3). Оператор break не дает возможности выйти из блока finally (§8.10). Если оператор break встречается внутри блока finally, цель оператора break должна находиться в том же блоке finally, в противном случае возникнет ошибка времени компиляции. Оператор break выполняется следующим образом. · Если оператор break производит выход из одного или нескольких блоков try, с которыми связаны соответствующие блоки finally, управление вначале передается в блок finally самого внутреннего оператора try. Если управление достигает конечной точки блока finally, после этого управление передается в блок finally следующего объемлющего оператора try. Этот процесс повторяется до тех пор, пока не будут выполнены все блоки finally всех сопутствующих операторов try. · Управление передается цели оператора break. Поскольку оператор break осуществляет безусловную передачу управления в другое место, конечная точка оператора break никогда не будет достижима.
|