MoveNext
Метод MoveNext объекта перечислителя инкапсулирует код блока итератора. При вызове метода MoveNext выполняется код в блоке итератора и устанавливается соответствующее свойство Current объекта перечислителя. Точное действие, выполняемое MoveNext, зависит от состояния объекта перечислителя при вызове MoveNext: · Если объект перечислителя находится в состоянии before, то при вызове MoveNext: o состояние меняется на running; o инициализируются параметры (включая this) блока итератора значениями аргументов и значением экземпляра, сохраненными при инициализации объекта перечислителя; o выполняется блок итератора от начала и до тех пор, пока выполнение не будет прервано (как описано ниже). · Если состояние объекта перечислителя running, результат вызова MoveNext не определен. · Если состояние объекта перечислителя suspended, при вызове MoveNext: o состояние меняется на running; o восстанавливаются значения всех локальных переменных и параметров (включая this) к значениям, сохраненным при последней приостановке выполнения блока итератора. Обратите внимание, что содержимое любых объектов, на которые ссылаются эти переменные, могло измениться со времени предыдущего вызова MoveNext; o возобновляется выполнение блока итератора с оператора, непосредственно следующего за оператором yield return, вызвавшим приостановку выполнения, и продолжается до тех пор, пока выполнение не будет прервано (как описано ниже). · Если состояние объекта перечислителя after, при вызове MoveNext возвращается false. Когда MoveNext выполняет блок итератора, выполнение может быть прервано четырьмя способами: оператором yield return, оператором yield break, по концу блока итератора и инициированным исключением, распространенным из блока итератора. · Если встретился оператор yield return (§8.14): o выражение, заданное в операторе, вычисляется, неявно преобразуется к типу yield и присваивается свойству Current объекта перечислителя; o выполнение тела итератора приостанавливается. Значения всех локальных переменных и параметров (включая this) сохраняются, как и место этого оператора yield return. Если оператор yield return находится внутри одного или более блоков try, их соответствующие блоки finally не; выполняются в это время; o состояние объекта перечислителя меняется на suspended; o метод MoveNext возвращает true вызвавшему его, указывая этим, что итерация успешно продвинулась к следующему значению. · Если встретился оператор yield break (§8.14): o если оператор yield break находится внутри одного или более блоков try, соответствующие им блоки finally выполняются; o состояние объекта перечислителя меняется на after; o метод MoveNext возвращает false вызвавшему его, указывая этим, что итерация выполнена. · Если встретился конец тела итератора: o состояние объекта перечислителя меняется на after; o метод MoveNext возвращает false вызвавшему его, указывая этим, что итерация выполнена. · Если инициируется исключение и распространяется из блока итератора: o соответствующие блоки finally в теле итератора будут выполняться распространением исключения; o состояние объекта перечислителя меняется на after; o распространение исключения продолжается до вызвавшего метод MoveNext. Current На свойство Current объекта перечислителя воздействуют операторы yield return в блоке итератора. Если объект перечислителя находится в состоянии suspended, значением Current является значение, установленное предыдущим вызовом MoveNext. Если объект перечислителя находится в состоянии before, running или after, результат обращения к Current не определен. Для итератора с типом выдачи, отличным от object, результат обращения к Current посредством реализации IEnumerable объекта перечислителя соответствует обращению к Current посредством реализации IEnumerator<T> объекта перечислителя и приведением результата к object. Dispose Метод Dispose используется для очистки итерации приведением объекта перечислителя в состояние after. · Если состояние объекта перечислителя before, вызов Dispose меняет это состояние на after. · Если состояние объекта перечислителя running, результат вызова Dispose не определен. · Если состояние объекта перечислителя suspended, при вызове Dispose: o состояние меняется на running; o выполняются все блоки finally, как если бы последний выполненный оператор yield return был оператором yield break. Если это приводит к инициированию и распространению из тела итератора исключения, состояние объекта перечислителя устанавливается в after, а исключение распространяется до объекта, вызвавшего метод Dispose; o состояние меняется на after. · Если состояние объекта перечислителя after, вызов Dispose не оказывает влияние. 10.14.5 Перечислимые объекты; Если член функции, возвращающий перечислимый тип интерфейса, реализован с помощью блока итератора, при вызове члена функции не происходит немедленное выполнение кода в блоке итератора. Вместо этого создается и возвращается перечислимый объект. Метод GetEnumerator перечислимого объекта возвращает объект перечислителя, который инкапсулирует код, указанный в блоке итератора, а выполнение кода в блоке итератора происходит при вызове метода MoveNext объекта перечислителя. Перечислимый объект имеет следующие характеристики: · Он реализует IEnumerable и IEnumerable<T>, где T – тип выдачи итератора. · Он инициализируется копией значений аргумента (при их наличии) и значением экземпляра, переданным члену функции. Обычно перечислимый объект является экземпляром созданного компилятором перечислимого класса, который инкапсулирует код в блоке итератора и реализует перечислимые интерфейсы, но возможны и другие методы реализации. Если перечислимый класс создан компилятором, этот класс будет вложен, прямо или косвенно, в класс, содержащий член функции, у него будет частная доступность и имя, зарезервированное для использования компилятором (§2.4.2). Перечислимый объект может реализовать больше интерфейсов, чем указано выше. В частности, перечислимый объект может также реализовать IEnumerator и IEnumerator<T>, позволяя ему выступать в роли как перечислимого, так и перечислителя. При таком типе реализации при первом вызове метода GetEnumerator перечислимого объекта возвращается сам перечислимый объект. При последующих вызовах GetEnumerator для перечислимого объекта, при их наличии, будет возвращена копия перечислимого объекта. Таким образом, каждый возвращенный перечислитель имеет собственное состояние, и изменения в одном перечислителе не влияют на другой. GetEnumerator Перечислимый объект предоставляет реализацию методов GetEnumerator интерфейсов IEnumerable и IEnumerable<T>. Два метода GetEnumerator совместно используют общую реализацию, которая получает и возвращает доступный объект перечислителя. Объект перечислителя инициализируется значениями аргументов и значением экземпляра, сохраненными при инициализации перечислимого объекта, но в других отношениях объект перечислителя функционирует так, как описано в §10.14.4. 10.14.6 Пример реализации; В этом разделе описана возможная реализация итераторов при использования стандартных конструкций C#. В основе реализации, описанной в этом разделе, лежат принципы, используемые в компиляторе Microsoft C#, однако она никоим образом не является обязательной или единственной возможной. Следующий класс Stack<T> реализует метод GetEnumerator с помощью итератора. Итератор перечисляет элементы стека в нисходящем порядке. using System; class Stack<T>: IEnumerable<T> public void Push(T item) { public T Pop() { public IEnumerator<T> GetEnumerator() { Метод GetEnumerator можно транслировать в экземпляр класса перечислителя, созданного компилятором и инкапсулирующего код в блоке итератора, как показано в следующем примере. class Stack<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() { class __Enumerator1: IEnumerator<T>, IEnumerator public __Enumerator1(Stack<T> __this) { public T Current { object IEnumerator.Current { public bool MoveNext() { public void Dispose() { void IEnumerator.Reset() { В предыдущей трансляции код в блоке итератора преобразован в конечный автомат и помещен в метод MoveNext класса перечислителя. Более того, локальная переменная i преобразована в поле в объекте перечислителя, что делает возможным ее существование при последующих вызовах MoveNext. Следующий пример служит для печати простой таблицы умножения целых чисел от 1 до 10. В этом примере метод FromTo возвращает перечислимый объект и реализуется с помощью итератора. using System; class Test static void Main() { Метод FromTo можно транслировать в экземпляр класса перечислителя, созданного компилятором и инкапсулирующего код в блоке итератора, как показано в следующем примере. using System; class Test static IEnumerable<int> FromTo(int from, int to) { class __Enumerable1: public __Enumerable1(int __from, int to) { public IEnumerator<int> GetEnumerator() { IEnumerator IEnumerable.GetEnumerator() { public int Current { object IEnumerator.Current { public bool MoveNext() { public void Dispose() { void IEnumerator.Reset() { Перечислимый класс реализует как перечислимые интерфейсы, так и интерфейсы перечислителя, что позволяет ему служить как перечислимым классом, так и перечислителем. При первом вызове метода GetEnumerator будет возвращен сам перечислимый объект. При последующих вызовах GetEnumerator для перечислимого объекта, при их наличии, будет возвращена копия перечислимого объекта. Таким образом, каждый возвращенный перечислитель имеет собственное состояние, и изменения в одном перечислителе не влияют на другой. Метод Interlocked.CompareExchange используется для обеспечения потокобезопасной операции. Параметры from и to преобразуются в поля в перечислимом классе. Поскольку параметр from изменяется в блоке итератора, то дополнительно вводится поле __from, которое содержит начальное значение, присваиваемое параметру from в каждом перечислителе. Метод MoveNext порождает исключение InvalidOperationException, если при его вызове значение __state равно 0. Это позволяет не допустить использования перечислимого объекта в качестве объекта перечислителя без вызова GetEnumerator. В следующем примере показан класс простого дерева. Класс Tree<T> реализует метод GetEnumerator с помощью итератора. Итератор перечисляет элементы дерева в инфиксном порядке. using System; class Tree<T>: IEnumerable<T> public Tree(T value, Tree<T> left, Tree<T> right) { public IEnumerator<T> GetEnumerator() { class Program static Tree<T> MakeTree<T>(params T[] items) { // The output of the program is: static void Main() { Tree<string> strings = MakeTree( Метод GetEnumerator можно транслировать в экземпляр класса перечислителя, созданного компилятором и инкапсулирующего код в блоке итератора, как показано в следующем примере. class Tree<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() { class __Enumerator1: IEnumerator<T>, IEnumerator public __Enumerator1(Node<T> __this) { public T Current { object IEnumerator.Current { public bool MoveNext() { case 0: case 1: __left_dispose: __yield_value: case 2: case 3: __right_dispose: __end: } public void Dispose() { case 1: case 3: } void IEnumerator.Reset() { Создаваемые компилятором временные данные, используемые в операторах foreach, переносятся в поля __left и __right объекта перечислителя. Поле __state объекта перечислителя обновляется таким образом, чтобы при создании исключения был правильно вызван необходимый метод Dispose(). Обратите внимание, что написание транслируемого кода с простыми операторами foreach невозможно.
11. Структуры; Структуры, как и классы, представляют определенным образом организованные данные. В структурах могут содержаться как данные, так и функции. Однако, в отличие от классов, структуры являются типами значений и для них не требуется выделение памяти из кучи. Переменная с типом структуры непосредственно содержит данные этой структуры, в то время как переменная с типом класса содержит ссылку на данные, которые считаются объектом. Структуры особенно удобны для работы с небольшим объемом данных, имеющих семантику зна
|