Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

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;
using System.Collections;
using System.Collections.Generic;

class Stack<T>: IEnumerable<T>
{
T[] items;
int count;

public void Push(T item) {
if (items == null) {
items = new T[4];
}
else if (items.Length == count) {
T[] newItems = new T[count * 2];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
}
items[count++] = item;
}

public T Pop() {
T result = items[--count];
items[count] = default(T);
return result;
}

public IEnumerator<T> GetEnumerator() {
for (int i = count - 1; i >= 0; --i) yield return items[i];
}
}

Метод GetEnumerator можно транслировать в экземпляр класса перечислителя, созданного компилятором и инкапсулирующего код в блоке итератора, как показано в следующем примере.

class Stack<T>: IEnumerable<T>
{
...

public IEnumerator<T> GetEnumerator() {
return new __Enumerator1(this);
}

class __Enumerator1: IEnumerator<T>, IEnumerator
{
int __state;
T __current;
Stack<T> __this;
int i;

public __Enumerator1(Stack<T> __this) {
this.__this = __this;
}

public T Current {
get { return __current; }
}

object IEnumerator.Current {
get { return __current; }
}

public bool MoveNext() {
switch (__state) {
case 1: goto __state1;
case 2: goto __state2;
}
i = __this.count - 1;
__loop:
if (i < 0) goto __state2;
__current = __this.items[i];
__state = 1;
return true;
__state1:
--i;
goto __loop;
__state2:
__state = 2;
return false;
}

public void Dispose() {
__state = 2;
}

void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}

В предыдущей трансляции код в блоке итератора преобразован в конечный автомат и помещен в метод MoveNext класса перечислителя. Более того, локальная переменная i преобразована в поле в объекте перечислителя, что делает возможным ее существование при последующих вызовах MoveNext.

Следующий пример служит для печати простой таблицы умножения целых чисел от 1 до 10. В этом примере метод FromTo возвращает перечислимый объект и реализуется с помощью итератора.

using System;
using System.Collections.Generic;

class Test
{
static IEnumerable<int> FromTo(int from, int to) {
while (from <= to) yield return from++;
}

static void Main() {
IEnumerable<int> e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}

Метод FromTo можно транслировать в экземпляр класса перечислителя, созданного компилятором и инкапсулирующего код в блоке итератора, как показано в следующем примере.

using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;

class Test
{
...

static IEnumerable<int> FromTo(int from, int to) {
return new __Enumerable1(from, to);
}

class __Enumerable1:
IEnumerable<int>, IEnumerable,
IEnumerator<int>, IEnumerator
{
int __state;
int __current;
int __from;
int from;
int to;
int i;

public __Enumerable1(int __from, int to) {
this.__from = __from;
this.to = to;
}

public IEnumerator<int> GetEnumerator() {
__Enumerable1 result = this;
if (Interlocked.CompareExchange(ref __state, 1, 0)!= 0) {
result = new __Enumerable1(__from, to);
result.__state = 1;
}
result.from = result.__from;
return result;
}

IEnumerator IEnumerable.GetEnumerator() {
return (IEnumerator)GetEnumerator();
}

public int Current {
get { return __current; }
}

object IEnumerator.Current {
get { return __current; }
}

public bool MoveNext() {
switch (__state) {
case 1:
if (from > to) goto case 2;
__current = from++;
__state = 1;
return true;
case 2:
__state = 2;
return false;
default:
throw new InvalidOperationException();
}
}

public void Dispose() {
__state = 2;
}

void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}

Перечислимый класс реализует как перечислимые интерфейсы, так и интерфейсы перечислителя, что позволяет ему служить как перечислимым классом, так и перечислителем. При первом вызове метода GetEnumerator будет возвращен сам перечислимый объект. При последующих вызовах GetEnumerator для перечислимого объекта, при их наличии, будет возвращена копия перечислимого объекта. Таким образом, каждый возвращенный перечислитель имеет собственное состояние, и изменения в одном перечислителе не влияют на другой. Метод Interlocked.CompareExchange используется для обеспечения потокобезопасной операции.

Параметры from и to преобразуются в поля в перечислимом классе. Поскольку параметр from изменяется в блоке итератора, то дополнительно вводится поле __from, которое содержит начальное значение, присваиваемое параметру from в каждом перечислителе.

Метод MoveNext порождает исключение InvalidOperationException, если при его вызове значение __state равно 0. Это позволяет не допустить использования перечислимого объекта в качестве объекта перечислителя без вызова GetEnumerator.

В следующем примере показан класс простого дерева. Класс Tree<T> реализует метод GetEnumerator с помощью итератора. Итератор перечисляет элементы дерева в инфиксном порядке.

using System;
using System.Collections.Generic;

class Tree<T>: IEnumerable<T>
{
T value;
Tree<T> left;
Tree<T> right;

public Tree(T value, Tree<T> left, Tree<T> right) {
this.value = value;
this.left = left;
this.right = right;
}

public IEnumerator<T> GetEnumerator() {
if (left!= null) foreach (T x in left) yield x;
yield value;
if (right!= null) foreach (T x in right) yield x;
}
}

class Program
{
static Tree<T> MakeTree<T>(T[] items, int left, int right) {
if (left > right) return null;
int i = (left + right) / 2;
return new Tree<T>(items[i],
MakeTree(items, left, i - 1),
MakeTree(items, i + 1, right));
}

static Tree<T> MakeTree<T>(params T[] items) {
return MakeTree(items, 0, items.Length - 1);
}

// The output of the program is:
// 1 2 3 4 5 6 7 8 9
// Mon Tue Wed Thu Fri Sat Sun

static void Main() {
Tree<int> ints = MakeTree(1, 2, 3, 4, 5, 6, 7, 8, 9);
foreach (int i in ints) Console.Write("{0} ", i);
Console.WriteLine();

Tree<string> strings = MakeTree(
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun");
foreach (string s in strings) Console.Write("{0} ", s);
Console.WriteLine();
}
}

Метод GetEnumerator можно транслировать в экземпляр класса перечислителя, созданного компилятором и инкапсулирующего код в блоке итератора, как показано в следующем примере.

class Tree<T>: IEnumerable<T>
{
...

public IEnumerator<T> GetEnumerator() {
return new __Enumerator1(this);
}

class __Enumerator1: IEnumerator<T>, IEnumerator
{
Node<T> __this;
IEnumerator<T> __left, __right;
int __state;
T __current;

public __Enumerator1(Node<T> __this) {
this.__this = __this;
}

public T Current {
get { return __current; }
}

object IEnumerator.Current {
get { return __current; }
}

public bool MoveNext() {
try {
switch (__state) {

case 0:
__state = -1;
if (__this.left == null) goto __yield_value;
__left = __this.left.GetEnumerator();
goto case 1;

case 1:
__state = -2;
if (!__left.MoveNext()) goto __left_dispose;
__current = __left.Current;
__state = 1;
return true;

__left_dispose:
__state = -1;
__left.Dispose();

__yield_value:
__current = __this.value;
__state = 2;
return true;

case 2:
__state = -1;
if (__this.right == null) goto __end;
__right = __this.right.GetEnumerator();
goto case 3;

case 3:
__state = -3;
if (!__right.MoveNext()) goto __right_dispose;
__current = __right.Current;
__state = 3;
return true;

__right_dispose:
__state = -1;
__right.Dispose();

__end:
__state = 4;
break;

}
}
finally {
if (__state < 0) Dispose();
}
return false;
}

public void Dispose() {
try {
switch (__state) {

case 1:
case -2:
__left.Dispose();
break;

case 3:
case -3:
__right.Dispose();
break;

}
}
finally {
__state = 4;
}
}

void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}

Создаваемые компилятором временные данные, используемые в операторах foreach, переносятся в поля __left и __right объекта перечислителя. Поле __state объекта перечислителя обновляется таким образом, чтобы при создании исключения был правильно вызван необходимый метод Dispose(). Обратите внимание, что написание транслируемого кода с простыми операторами foreach невозможно.


 

11. Структуры;

Структуры, как и классы, представляют определенным образом организованные данные. В структурах могут содержаться как данные, так и функции. Однако, в отличие от классов, структуры являются типами значений и для них не требуется выделение памяти из кучи. Переменная с типом структуры непосредственно содержит данные этой структуры, в то время как переменная с типом класса содержит ссылку на данные, которые считаются объектом.

Структуры особенно удобны для работы с небольшим объемом данных, имеющих семантику зна







Дата добавления: 2015-09-07; просмотров: 589. Нарушение авторских прав; Мы поможем в написании вашей работы!




Кардиналистский и ординалистский подходы Кардиналистский (количественный подход) к анализу полезности основан на представлении о возможности измерения различных благ в условных единицах полезности...


Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...


Композиция из абстрактных геометрических фигур Данная композиция состоит из линий, штриховки, абстрактных геометрических форм...


Важнейшие способы обработки и анализа рядов динамики Не во всех случаях эмпирические данные рядов динамики позволяют определить тенденцию изменения явления во времени...

Принципы резекции желудка по типу Бильрот 1, Бильрот 2; операция Гофмейстера-Финстерера. Гастрэктомия Резекция желудка – удаление части желудка: а) дистальная – удаляют 2/3 желудка б) проксимальная – удаляют 95% желудка. Показания...

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

Билиодигестивные анастомозы Показания для наложения билиодигестивных анастомозов: 1. нарушения проходимости терминального отдела холедоха при доброкачественной патологии (стенозы и стриктуры холедоха) 2. опухоли большого дуоденального сосочка...

Меры безопасности при обращении с оружием и боеприпасами 64. Получение (сдача) оружия и боеприпасов для проведения стрельб осуществляется в установленном порядке[1]. 65. Безопасность при проведении стрельб обеспечивается...

Весы настольные циферблатные Весы настольные циферблатные РН-10Ц13 (рис.3.1) выпускаются с наибольшими пределами взвешивания 2...

Хронометражно-табличная методика определения суточного расхода энергии студента Цель: познакомиться с хронометражно-табличным методом опреде­ления суточного расхода энергии...

Studopedia.info - Студопедия - 2014-2024 год . (0.011 сек.) русская версия | украинская версия