Итераторы в C#
Для обращения к элементам списка можно реализовать интерфейсы IEnumerator и IEnumerable. В последних версиях Visual Studio эти интерфейсы, часто, реализуются автоматически компилятором. Но можно использовать итератор, который представляет собой метод, оператор или аксессор, возвращающий по очереди члены совокупности объектов от ее начала и до конца. Отличие итератора от индекса заключается в том, что итератор не связан с типом списка и позволяет обращаться к элементам связного списка так же, как и к элементам обычного массива. Более того, при создании коллекций с применением итераторов вся коллекция сразу не создается, формируется только текущий элемент.
вставка
У итераторов есть встроенные машины состояний. Используя новое ключевое слово yield, ваша программа может возвращать значения в оператор foreach, вызвавший итератор. В следующий раз, когда снова вызывает итератор, он начинает выполнение с того места, на котором остановился предыдущий оператор yield. В следующем примере ваша программа итерирует по трем строковыым типам:
public class List// Не работает
{
internal object[] elements;
internal int count;
public string foreach()
{
yield "microsoft";
yield "corporation";
yield "developer division";
}
}
В следующем примере цикл foreach, который вызывает этот итератор, будет выполнен три раза, каждый раз получая строки в порядке, определенном тремя предыдущими операторами yield:
List list = new List();
foreach(string s in list)
{
Console.WriteLine(s);
}
Если вы хотите, чтобы программа реализовывала итератор для прохода элементов списка, вы должны модифицировать итератор так, чтобы он проходил через массив элементов, используя цикл foreach, возвращая каждый элемент массива в каждой итерации:
public class List
{
internal object[] elements;
internal int count;
public object foreach()
{
foreach(object o in elements)
{
yield o;
}
}
}
Как работают итераторы
Итератором является метод, который формирует перечисляемые последовательности, используя специальное ключевое слово yield. В примере ниже показан класс, в котором реализован итератор. Выводятся числа нечетные числа от 1 до 11.
class MyClass
{
int last = 0;
public MyClass(int last) { this.last = last; }
public IEnumerable<int> NumColl(int beg)
{
for (int i = beg; i <= last; i++)
yield return 2 * i + 1;
}
}
class Program
{
static void Main(string[ ] args)
{
MyClass mc = new MyClass(5);
foreach (int i in mc. NumColl(0))
Console.Write("{0} ", i);
Console.ReadKey();
}
}
Вывод программы
Итератор похож на обычный метод, но различен способ возврата значения. Итератор в примере имеет код возврата IEnumerable<int>, и не видно, чтобы он что-либо возвращал этого типа. Он не содержит обычного оператора возврата, и содержит только оператор yield return, но он возвращает одиночное число типа int, а не коллекцию. Итераторы формируют значения по одному за раз с помощью оператора yield return, и в отличие от обычного возврата метод продолжает выполняться, пока либо достигнет конца, либо будет остановлен преждевременно с помощью оператора yield break, либо возникнет исключительная операция. Каждый оператор формирует значение, которое вставляется в последовательность. В следующем примере это показано более четко, где формируются числа 1 ÷ 3.
Очень простой итератор
public static IEnumerable<int> ThreeNumbers()
{
yield return 1;
yield return 2;
yield return 3;
}
Хотя это простая концепция, способ реализации в чем-то отличен, поскольку итераторы не выполняются таким же способом, как и другой код. Напомним, что с помощью IEnumerable<T> вызывающая программа занята при извлечении следующего значения. Цикл foreach получает перечислитель и повторяет вызов метода MoveNext() до тех пор, пока не будет возвращено значение false и не будет обращения к свойству Current для получения текущего значения. Однако значения перечислителя не хранятся в списке типа List<T>
Ниже приведен простой пример итератора, в котором вместо явной реализации интерфейсов IEnumerator и IEnumerable применяется итератор.
// Простой пример применения итератора.
class MyClass
{
string[ ] strs = { "Anchor", "Broadcast", "City", "Danger" };
// Этот итератор возвращает строки из массива strs в классе MyClass.
public IEnumerator<string> GetEnumerator()
{
foreach (string st in strs)
yield return st;
}
}
class Iterator
{
static void Main()
{
MyClass mc = new MyClass();
foreach (string st in mc) Console.WriteLine(st + " ");
Console.ReadKey();
}
}
При выполнении этой программы получается следующий результат.
Как видите, выполнено последовательное обращение к элементам массива strs в объекте mc класса MyClass.
Рассмотрим эту программу более подробно. Во-первых, обратите внимание на то, что в классе MyClass не указывается IEnumerator в качестве реализуемого интерфейса. При создании итератора компилятор реализует этот интерфейс автоматически. И, во-вторых, обратите особое внимание на метод GetEnumerator (), который ради удобства приводится ниже еще раз.
Итераторы в C#
Лекции по предмету «Программирование»