6_3_C#_итераторы.pptx
- Количество слайдов: 11
Оператор foreach итерационная переменная оператор foreach (type identifier in expression) statement тип итерационной переменной массив или коллекция ü Коллекция (expression) • должна иметь тип class, struct или interface; • в типе коллекции достаточно определить открытый экземплярный метод Get. Enumerator(). ü В типе, который возвращает метод Get. Enumerator(), должны быть определены • свойство Current { get; }, которое дает доступ к текущему элементу коллекции; • метод bool Move. Next(), который возвращает значение • true - если в коллекции еще есть элементы; • false - в противном случае. ü Тип свойства Current (тип элементов коллекции) должен допускать неявное
Соглашения о реализации итераторов ü При инициализации итератор(enumerator) позиционируется перед первым элементом коллекции. Вызов свойства Current в этой позиции порождает исключение. Необходимо вызвать Move. Next перед тем, как обратиться к Current. Метод Reset возвращает итератор в позицию инициализации. ü Метод Move. Next перемещает Current к следующему элементу. Свойство Current возвращает один и тот же объект до тех пор, пока не будет вызван один из методов Move. Next или Reset. ü Move. Next возвращает false, когда итератор позиционируется за последним элементом коллекции. Если вызов Move. Next возвращает false, следующий за ним вызов свойства Current бросает исключение. ü Итераторы используются только для чтения данных из коллекции, их нельзя использовать для изменения коллекции (нельзя изменять ссылки).
Интерфейсы System. IEnumerable и System. Collections. IEnumerator ü В классе, реализующем интерфейс IEnumerable, можно использовать оператор foreach. ü Интерфейс IEnumerable реализован в классе System. Array и классахколлекциях. public interface IEnumerable { IEnumerator Get. Enumerator(); } public interface IEnumerator { object Current {get; } bool Move. Next(); void Reset(); } ü Типы, реализующие интерфейс IEnumerator, называют итераторами или перечислителями (enumerators).
Реализация итератора. Пример ü В примере для класса Library. Class определен итератор, который осуществляет перебор читателей (объектов типа Reader) c непустым списком книг. partial class Library. Class : IEnumerable { static Array. List books = new Array. List(); Array. List readers = new Array. List(); class Readers. With. Books. Enumerator : IEnumerator { private int current = -1; Library. Class lib; public static Array. List Books { get { return books; } } public Array. List Readers { get { return readers; } } public object Current { get { return lib. Readers[current] ; } } public IEnumerator Get. Enumerator() { return new Readers. With. Books. Enumerator(this); } } public Readers. With. Books. Enumerator(Library. Class lib) { this. lib = lib; } public bool Move. Next() { while (current < lib. Readers. Count-1) { current++; Reader rd = lib. Readers[current] as Reader; if (rd. Reader_books. Count > 0) return true; } return false; } public void Reset() { current = -1; } partial class Reader : Person { public int id {get; set; } Array. List reader_books = new Array. List(); } public Array. List Reader_books { get { return reader_books; } } }
Именованные итераторы ü В классе можно реализовать несколько итераторов. ü В следующем примере в классе Library. Class определены два именованных итератора partial class Library. Class : IEnumerable { public IEnumerable Books. Iterator() { return books; } } public IEnumerable Readers. Iterator() { return readers; } ü В следующем примере оператор foreach выполняется для итератора по умолчанию и двух именованных итераторов Console. Write. Line("n Default Iterator"); foreach (object reader in lib) Console. Write. Line(reader); Console. Write. Line("n Books. Iterator"); foreach (Book book in lib. Books. Iterator()) Console. Write. Line(book); Console. Write. Line("n Readers. Iterator"); foreach (Reader reader in lib. Readers. Iterator()) Console. Write. Line(reader. To. Short. String());
Итераторы с параметрами ü Итераторы могут иметь параметры. В следующем примере в классе Library. Class определены два итератора с параметрами – один для перебора книг, год издания которых больше или равен значению параметра, другой для перебора книг, в названии которых есть заданная строка. partial class Library. Class : IEnumerable { public IEnumerable Books. Year. Iterator(int year) { foreach (Book book in books) { if (book. Year >= year) yield return book; } } } public IEnumerable Books. Sub. Title. Iterator(string sub. Title) { foreach (Book book in books) { if (book. Title. Contains(sub. Title)) yield return book; } } ü В следующем примере для этих итераторов выполняется оператор foreach (Book book in lib. Books. Year. Iterator(2008)) Console. Write. Line(book); foreach (Book book in lib. Books. Sub. Title. Iterator("C#")) Console. Write. Line(book);
Итерация с помощью оператора yield ü Для итератора, который выполняет итерацию по объекту класса с помощью оператора yield, компилятор автоматически создает методы Current, Move. Next и Dispose интерфейса IEnumerator или IEnumerator
Оператор yield. Пример 1 ü В примере оператор yield используется в итераторе для перебора всех полей типа Book. partial class Book { public string Title { get; set; } public string Author { get; set; } public int Year { get; set; } public Book(string Title, string Author, int Year) { this. Title = Title; this. Author = Author; this. Year = Year; } } public IEnumerable Fields. Iterator() { Console. Write. Line("1"); yield return Title; Console. Write. Line("2"); yield return Author; Console. Write. Line("3"); yield return Year; Console. Write. Line("4"); } static void Main(string[] args) { Book bk = new Book("C#", "Эндрю Троелсен", 2005); foreach (object fld in bk. Fields. Iterator()) Console. Write. Line(fld); } Вывод: 1 C# 2 Эндрю Троелсен 3 2005 4
Оператор yield. Пример 2 ü В примере оператор yield используется в итераторе для перебора данных типа Reader. static void Main(string[] args) { partial class Reader : Person, IEnumerable { Reader r 1 = new Reader ( new Person("Ivan", "Ivanov", new Date. Time(1990, 1, 1)), public int id {get; set; } public Date. Time Registration. Date { get; private set; } Array. List reader_books = new Array. List(); } public IEnumerator Get. Enumerator() { int j = 0; Console. Write. Line("_1"); yield return id; Console. Write. Line("_2"); yield return Registration. Date; Console. Write. Line("_3"); foreach (Book book in reader_books) { Console. Write. Line("in foreach " + j++); yield return book; } Console. Write. Line(j); } 123, new Date. Time(2008, 01 , 09)); r 1. Add(new Book("C# ", "Trey Nash", 2010)); r 1. Add(new Book("C++", "Bjarn Straustroup", 2008)); foreach (object obj in r 1) Console. Write. Line(obj); } Вывод: _1 123 _2 09. 01. 2008 0: 00 _3 in foreach 0 C# Trey Nash 2010 in foreach 1 C++ Bjarn Straustroup 2008 2
Оператор yield. Пример 3 (выражение yeild break; ) ü В примере в классе Library. Class определен итератор для перебора первых трех читателей с непустым списком книг. partial class Library. Class : IEnumerable { static Array. List books = new Array. List(); Array. List readers = new Array. List(); public IEnumerable First. Three. Readers. Iterator() { int j = 0; foreach (Reader reader in readers) { if ( j == 3) yield break; if (reader. Reader_books. Count > 0) { j++; yield return reader; } }
Ограничения на методы с оператором yield ü Оператор yield может располагаться только внутри блока итератора. ü В методах, содержащих yield : • не допускается использование блоков unsafe; • параметры метода, оператора или метода доступа не могут иметь модификаторов ref или out; • выражение yield return