7_C#_IComparable_IEnumerable.pptx
- Количество слайдов: 18
Интерфейсы System. IComparable, System. Collections. IComparer ü Если элементы коллекции реализуют интерфейс IComparable или IСomparer, можно использовать методы Sort для сортировки коллекции. • В System. Array 8 перегрузок статического метода Sort. • B System. Collections. Array. List 3 экземплярных метода Sort. public interface IComparable { int Compare. To( object y ); } public interface IComparer { int Compare( object x, object y ); } Возвращаемое значение: <0 x<y 0 x==y >0 x>y ( в методе Compare. To x – вызывающий объект) ü Интерфейс IComparable реализован в string, enum, double, Int 32, …
Реализация System. IComparable в типах Double и Single ü Интерфейс IComparable реализован в типах float, double. ü Возвращаемое значение мeтодов int Compare. To (object value) : отрицательно 0 положительно • значение вызывающего объекта меньше value ; • вызывающий объект - Na. N, value – число. • значение вызывающего объекта равно value ; • и вызывающий объект, и value имеют значение Na. N, бесконечность или минус бесконечность. • значение вызывающего объекта больше value ; • вызывающий объект - число, value равно Na. N ; • value равно null.
Реализация System. IComparable в типе string ü Интерфейс IComparable реализован в типе string. Сравнение выполняется с учетом регистра и текущих региональных установок (current culture). ü Возвращаемое значение в мeтоде int Compare. To (object value); отрицательно 0 положительно • значение вызывающего объекта меньше value ; • значение вызывающего объекта равно value ; • значение вызывающего объекта больше value ; • value равно null.
Методы Sort в System. Array ü В System. Array определено 8 перегруженных методов Sort() для сортировки одномерных массивов. ü В элементах одномерного массива должен быть реализован один из интерфейсов IComparable или IComparer public static void Sort( Array array ); // сортирует массив c // использованием IComparable public static void Sort( Array array, IComparer comparer ); // сортирует c // использованием IComparer ( если comparer==null, // используется IComparable) public static void Sort ( Array array, int index, int length ); // сортирует подмножество элементов массива public static void Sort( Array keys, Array items ); // сортирует первый // массив и переставляет элементы второго в среднем // за O(nlog n) операций, значение items может быть // равно null.
Методы Sort в Array. List ü Сортировка всей коллекции с использованием метода Compare. To() интерфейса IComparable: public virtual void Sort(); ü Сортировка всей коллекции или ее части с использованием метода Compare() интерфейса IComparer: public virtual void Sort( IComparer comparer ); public virtual void Sort( int index, int count, IComparer comparer );
Реализация IComparable и IComparer. Пример public class Person : IComparable, IComparer { public string first_name { get; set; } public string second_name { get; set; } public Date. Time date { get; set; } public Person ( string first_name = "None", string second_name = "None", Date. Time date = new Date. Time()) { this. first_name = first_name; this. second_name = second_name; this. date = date; } public int Compare. To(object obj) { return second_name. Compare. To((obj as Person). second_name); } public int Compare(object x, object y) { return ((x as Person). date). Compare. To((y as Person). date); } } public override string To. String() { return first_name + " " + second_name + " " + date. To. Short. Date. String(); }
Реализация IComparable и IComparer. Пример (продолжение) static void Main(string[] args) { Вывод: Array. List alist = new Array. List(); alist. Add(new Person("Иван", "Иванов", new Date. Time(1990, 8, 7))); Сортировка по фамилии alist. Add(new Person("Сергей", "Петров", new Date. Time(1988, 8, 17))); Алексей Алексеев 12. 1990 alist. Add(new Person("Алексей", "Алексеев", new Date. Time(1990, 12))); Иванов 07. 08. 1990 Сергей Петров 17. 08. 1988 Console. Write. Line("n. Сортировка по фамилии"); alist. Sort(); Сортировка по дате рождения for (int j = 0; j < alist. Count; j++) Console. Write. Line(alist[j]); Сергей Петров 17. 08. 1988 Иванов 07. 08. 1990 Console. Write. Line("n. Сортировка по дате рождения"); alist. Sort(new Person()); for (int j = 0; j < alist. Count; j++) Console. Write. Line(alist[j]); } Алексей Алексеев 12. 1990
Оператор 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<T>. Метод Reset() не поддерживается. Для повторной итерации необходимо получить новый итератор. ü Компилятор определяет для итератора вложенный класс, который отслеживает положения итератора, пока в клиентском коде выполняется цикл foreach. ü Ключевое слово yield можно использовать только в блоке итератора и только в одной из следующих форм: yield return <expression>; yield break; ü Оператор yield return <expression> вычисляет выражение expression и возвращает значение объекту перечислителя; выражение expression должно допускать неявное преобразование к типу результата итератора. ü Оператор yield break завершает итерацию.
Оператор 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<expression> не может размещаться в блоке catch или в блоке try, который имеет обработчики catch. ü Оператор yield не может использоваться в анонимных методах.
7_C#_IComparable_IEnumerable.pptx