Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
30-36.docx
Скачиваний:
1
Добавлен:
25.09.2019
Размер:
40.51 Кб
Скачать

30. Стандартные интерфейсы. Создание методов итератора yield.

Язык C# содержит удобную синтаксическую конструкцию foreach для перебора элементов пользовательского типа. Чтобы поддерживать перебор при помощи foreach, тип должен реализовывать интерфейс IEnumerable. Кодирование поддержки этого интерфейса упрощается с использованием итераторов. Итератор (iterator) – это блок кода, который порождает упорядоченную последовательность значений. Итератор отличает присутствие в блоке кода одного или нескольких операторов yield. Оператор yield return <выражение> возвращает следующее значение в последовательности, оператор yield break прекращает генерирование последовательности.

Итераторы могут использоваться в качестве тела функции, если тип возвращаемого значения функции – это тип, реализующий или наследованный от интерфейсов IEnumerator, IEnumerator<T>, IEnumerable, IEnumerable<T>.

Рассмотрим пример использования итераторов. Следующее приложение выводит на консоль таблицу умножения:

using System;

using System.Collections;

class Test {

static IEnumerable FromTo(int from, int to) {

while (from <= to) yield return from++;

}

static void Main() {

IEnumerable e = FromTo(1, 10);

foreach (int x in e) {

foreach (int y in e) {

Console.Write("{0,3} ", x * y);

}

Console.WriteLine();

}

}

}

31. Стандартные интерфейсы. Создание клонируемых объектов iCloneable.

Реализуя интерфейс ICloneable, вы позволяете создавать копию объекта. В интерфейсе ICloneable определён только один метод:

Object Clone ()

Этот метод создаёт копию вызывающего объекта. От того, как реализован метод, зависит вид создаваемой копии. Существует два вида копий: детальная и поверхностная. При создании копии первого вида копия и оригинал совершенно независимы. Следовательно, если исходный объект содержит ссылку на другой объект О, то в результате детального копирования будет также создана копия объекта О. В поверхностной копии копируются члены, но не объкты, на которые ссылаются эти члены. Если объект ссылается на другой объект О, то по окончании поверхностного копирования как копия, так и исходный объект будут ссылаться на один и тот же объект О, и любые изменения, вносимые в объект О, отразятся и на копии, и на оригинале. Обычно метод Clone () реальзуется так, чтобы выполнялось детальное копирование. Поверхностные копии можно создавать с помощью метода MemberwiseClone(), который определён в классе Object.

Рассмотрим пример, который иллюстрирует использование интерфейса ICloneable. В следующей программе создаётся класс Test, который содержит ссылку на класс X. В классе Test для создания детальной копии используется метод Clone().

Class X {

public int a;

public X(int x) {

a = x;

}

}

Class Test : ICloneable {

public X o;

public int b;

public Test (int x, int y) {

o = new X(x);

b = y;

}

Public void show(string name) {

Console. Write(“Значения объекта “ + name + “ : “);

Console. WriteLine(“o.a: {0}, b: {1}”, o.a, b);

}

// Создаём детальную копию вызывающего объекта.

Public object Clone () {

Test temp = new Test (o.a, b);

Return temp;

}

}

Class CloneDemo {

Public static void Main () {

Test ob1 = new Test(10,20);

Obl. Show(“ob1”);

Console.WriteLine (“ Создаём объект ob2 как клон объекта ob1.”);

Test ob2 = (Test) ob1. Clone();

Ob2.show(“ob2”);

Console.WriteLine (“Заменяем член ob1.o.a числом 99, “ + “ а член ob1.b числом 88.”);

Ob1.o.a = 99;

Ob1.b = 88;

Ob1.show(“ob1”);

Ob2.show(“ob2”);

}

}

Объект ob2 является копией объекта ob1, но ob1 и ob2 - отдельные объекты. Изменение одного никак не отражается на другом. Это достигается за счёт того, что для копии создаётся новый объект Х, которому присваивается то же значение, которое имеет объект Х в оригинале.

32. Стандартные интерфейсы. Создание сравнимых обьектов IComparable.

Имея в своем распоряжении класс CStudent, мы можем организовать массив из объектов данного класса. Однако методы сортировки из класса Array для такого массива работать не будут. Для того чтобы выполнялась сортировка, необходимо, чтобы класс реализовывал интерфейс System.IComparable.

Интерфейс IComparable определен следующим образом:

interface IComparable {

int CompareTo(object o);

}

Метод CompareTo() сравнивает текущий объект с объектом o. Метод должен возвращать ноль, если объекты «равны», любое число больше нуля, если текущий объект «больше», и любое число меньше нуля, если текущий объект «меньше» сравниваемого.

Пусть мы хотим сортировать массив студентов по убыванию среднего балла. Внесем поддержку IComparable в класс CStudent:

class CStudent : IComparable {

. . .

public int CompareTo(object o) {

CStudent tmp = (CStudent) o;

if(ball > tmp.ball) return 1;

if(ball < tmp.ball) return -1;

return 0;

}

}

Класс CStudent теперь можно использовать таким образом:

CStudent[] Group = {new CStudent("Alex", 1, 7.0),

new CStudent("Ivan", 1, 9.0),

new CStudent("Anna", 1, 8.0),

new CStudent("Peter", 1, 5.0)};

Console.WriteLine("Unsorted group");

foreach (CStudent s in Group) {

Console.WriteLine(s);

}

Array.Sort(Group);

Array.Reverse(Group);

Console.WriteLine("Sorted group");

foreach (CStudent s in Group) {

Console.WriteLine(s);

}

Данная программа выводит следующее:

Unsorted group

Name = Alex; Course = 1; Ball = 7

Name = Ivan; Course = 1; Ball = 9

Name = Anna; Course = 1; Ball = 8

Name = Peter; Course = 1; Ball = 5

Sorted group

Name = Ivan; Course = 1; Ball = 9

Name = Anna; Course = 1; Ball = 8

Name = Alex; Course = 1; Ball = 7

Name = Peter; Course = 1; Ball = 5

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

Работу с событиями можно условно разделить на три этапа:

  • объявление события (publishing);

  • регистрация получателя события (subscribing);

  • генерация события (raising).

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

event <имя делегата> <имя события>;

Ключевое слово event указывает на объявление события. Объявление события может предваряться модификаторами доступа.

Приведем пример класса с объявлением события:

//Объявление делегата для события

delegate void Proc(int val);

class CEventClass {

int data;

//Объявление события

event Proc OnWrongData;

. . .

}

Фактически, события являются полями типа делегатов. Объявление события транслируется компилятором в следующий набор объявлений в классе:

  1. в классе объявляется private-поле с именем <имя события> и типом <имя делегата>;

  2. в классе объявляются два метода с именами add_<имя события> и remove_<имя события> для добавления и удаления обработчиков события.

Методы для обслуживания события содержат код, добавляющий (add_*) или удаляющий (remove_*) процедуру обработки события в цепочку группового делегата, связанного с событием.

Для генерации события в требуемом месте кода помещается вызов в формате <имя события>(<фактические аргументы>). Предварительно можно проверить, назначен ли обработчик события. Генерация события может происходить в одном из методов того же класса, в котором объявлено событие. Генерировать в одном классе события других классов нельзя.

Приведем пример класса, содержащего объявление и генерацию события. Данный класс будет включать метод с целым параметром, устанавливающий значение поля класса. Если значение параметра отрицательно, генерируется событие, определенное в классе:

delegate void Proc(int val);

class CExampleClass {

int field;

public event Proc onErrorEvent;

public void setField(int i){

field = i;

if(i < 0) {

if(onErrorEvent != null) onErrorEvent(i);

}

}

}

Рассмотрим этап регистрации получателя события. Для того чтобы отреагировать на событие, его надо ассоциировать с обработчиком события. Обработчиком события может быть метод-процедура, совпадающий по типу с типом события (делегатом). Назначение и удаление обработчиков события выполняется при помощи перегруженных версий операторов += и -=. При этом в левой части указывается имя события, в правой части – объект делегата, созданного на основе требуемого метода-обработчика.

Используем предыдущий класс CExampleClass и продемонстрируем назначение и удаление обработчиков событий:

class MainClass {

public static void firstReaction(int i) {

Console.WriteLine("{0} is a bad value!", i);

}

public static void secondReaction(int i) {

Console.WriteLine("Are you stupid?");

}

public static void Main() {

CExampleClass c = new CExampleClass();

c.setField(200);

c.setField(-200); // Нет обработчиков, нет и реакции

// Если бы при генерации события в CExampleClass

// отсутствовала проверка на null, то предыдущая

// строка вызвала бы исключительную ситуацию

// Назначаем обработчик

c.onErrorEvent += new Proc(firstReaction);

// Теперь будет вывод "-10 is a bad value!"

c.setField(-10);

// Назначаем еще один обработчик

c.onErrorEvent += new Proc(secondReaction);

// Вывод: "-10 is a bad value!" и "Are you stupid?"

c.setField(-10);

}

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]