Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky

.pdf
Скачиваний:
36
Добавлен:
22.03.2016
Размер:
2 Мб
Скачать
☆

логические функции, и построить для них таблицы истинности.

// 17_07.cs - свойство с типом делегата и анонимные методы using System;

public delegate bool BoolDel(bool x, bool y); // Делегат-тип. public class Create // Класс таблиц истинности.

{

BoolDel specificFun; // Поле, определяющее логическую функцию public BoolDel SpecificFun

{

set { specificFun = value; }

}

public bool[,] define() // Формирование логического массива

{

bool[,] res = new bool[4, 3]; bool bx, by;

int k = 0;

for (int i = 0; i <= 1; i++) for (int j = 0; j <= 1; j++)

{

bx = (i == 0 ? false : true); by = (j == 0 ? false : true); res[k, 0] = bx;

res[k, 1] = by;

res[k++, 2] = specificFun(bx, by);

}

return res;

}

}

public class Methods // Класс с методами

{

static public void printTabl(bool[,] tabl)

{

Console.WriteLine("A B F");

for (int i = 0; i < tabl.GetLength(0); i++) Console.WriteLine("{0} {1} {2}",

tabl[i, 0] ? 1 : 0, tabl[i, 1] ? 1 : 0, tabl[i, 2] ? 1 : 0);

}

}

class Program // Основной класс программы

{

static void Main()

{

Create create = new Create(); create.SpecificFun = delegate(bool a, bool b) { return a || b; };

Console.WriteLine("Таблица для (A || B):");

Methods.printTabl(create.define());

create.SpecificFun = delegate(bool d, bool e) { return d && !e; };

Console.WriteLine("\nТаблица для (A && !B):"); Methods.printTabl(create.define());

}

}

Результаты выполнения программы:

Таблица для (A || B): A B F

0 0 0

0 1 1

1 0 1

1 1 1

Таблица для (A && !B): A B F

0 0 0

0 1 0

1 0 1

1 1 0

17.6. События

Событие "событие" – средство, позволяющее объекту (или классу) послать во "внешний для объекта или класса мир" сообщение о переходе в некоторое новое состояние или о получении сообщения из внешнего источника. Так как на уровне программы все действия объектов и классов реализуются с помощью методов, то и посылка сообщения оформляется как оператор в теле некоторого метода.

Синтаксически оператор посылки сообщения "посылки сообщения" выглядит так:

имя_события (аргументы_для_делегата);

Разберем, о каком событии идет речь и какую роль играет делегат, которому нужно предоставить аргументы.

Отметим, что объявление события может размещаться в классе и в интерфейсе. Начнём с классов. Событие это член класса (или его объекта),

вводимый объявлением "событие:объявление события" \y "события" :

модификаторыopt event имя_делегата имя_события;

модификатором может быть abstract, new, override, static, virtual, public, protected, private, internal.

event – служебное слово "служебное слово: event" \y "служебное слово"

декларации события.

имя_события – идентификатор, выбираемый программистом в качестве названия

конкретного члена, называемого переменной события. В практике программирования на C# принято начинать имена событий с префикса on.

имя_делегата – имя того делегата-типа (его называют делегатом события или

событийным делегатом), который должен представлять событию те методы,

которые будут вызываться в ответ на посылку сообщения о событии.

Таким образом, событие это член класса, имеющий тип делегата. Этот

делегат-тип должен быть доступен в точке объявления события. Например, его определение должно быть в том же файле. Событийный делегат-тип определяет для события сигнатуру тех методов, которые могут быть вызваны в ответ на посылку сообщения о нём. Напомним, что в сигнатуру "сигнатура" , вводимую делегатом,

входит спецификация параметров метода и тип возвращаемого методом значения. В

соответствии с этой сигнатурой определяются типы аргументов в операторе посылки сообщения о событии. В качестве типа возвращаемого значения в событийном делегате обычно используется void.

В качестве примера определим статический метод, посылающий через каждую

секунду сообщение о событии. Чтобы такая возможность появилась, необходимо,

чтобы в классе, которому принадлежит метод, было объявлено событие, и был доступен соответствующий этому событию делегат-тип. Соответствующие

объявления могут быть такими:

 

delegate void ТimeHandler();

// объявление делегата-типа

static event ТimeHandler onTime; // объявление события

Рекомендуется в название событийного делегата включать в качестве

суффикса слово Handler (обработчик). Делегат ТimeHandler в соответствии с его объявлением предназначен "представлять" методы без параметров, с возвращаемым значением типа void (ничего не возвращающие в точку вызова). Событие с именем onTime "настроено" на работу с экземплярами делегата ТimeHandler.

В том же классе, где размещено объявление события и доступен делегат-тип,

можно определить метод, через каждую секунду "генерирующий" посылку

сообщений:

static void run() { // процесс с событиями Console.WriteLine("Для выхода нажмите Ctrl+C!");

while (true)

{ // бесконечный цикл

onTime();

// посылка сообщения о событии

System.Threading.Thread.Sleep(1000); // задержка на 1 секунду

}

 

}

 

В методе

run() бесконечный цикл, в каждой итерации которого оператор

onTime() посылает сообщение о событии, связанном с делегатом TimeHandler. Затем

вызывается статический метод Sleep() "метод:метод Sleep()"

класса Thread "класс:

Thread" \y "класс"

из пространства имен System.Threading

"пространство имен

System.Threading"

. Назначение этого метода состоит в "задержке" процесса

выполнения программы на количество миллисекунд, соответствующее значению аргумента. В данном случае задержка равна 1000 миллисекундам, то есть одной секунде.

Метод run(), посылая сообщения о событиях, "ничего не знает" о том, кто

будет получать эти сообщения, и как они будут обрабатываться. В технологии

Windows-программирования принято говорить, что объект (в нашем примере не

объект, а статический метод класса) публикует события "события:публикует

события" , посылая сообщения о них. Другие объекты (в нашем примере это будут статические методы) могут подписаться на события.

Подписка на получение сообщений о событии в языке C# предусматривает следующие действия:

создание экземпляра того делегата, на который настроено событие;

подключение экземпляра делегата к событию.

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

имя_события += new имя_делегата (имя_метода);

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

– наличие и доступность метода, который будет вызван для обработки события. Имя этого метода используется в качестве аргумента конструктора делегата. Само собой, и делегат события и имя события должны быть доступны в месте подписки.

На одно событие могут быть подписаны несколько методов, для каждого из которых нужно использовать свой оператор приведенного вида.

Предположим, что "принимать сообщения" о событиях в методе run() должен метод Main() того же класса, в котором определен метод run(). Пусть

обработчиками сообщения о событии должны быть два метода с заголовками: static void one( )

static void two( )

Тогда метод Main может быть таким (программа 17_08.cs): static void Main() {

onTime += new TimeHandler(one); // подписка на событие...

onTime += new TimeHandler(two); // ...для метода two run(); // запуск процесса

}

Остальное, то есть функциональность программы в целом, зависит от возможностей и особенностей методов one() и two(). В следующей ниже программе метод one() выводит в консольное окно дату и посекундно изменяющееся значение времени. Метод two() в начале той же строки выводит порядковый номер события.

Номер изменяется каждую секунду (при каждом обращении к методу).

// 17_08.cs - статические события и статические методы using System;

delegate void TimeHandler( ); // объявление делегата-типа class test_cs {

static event TimeHandler onTime;// объявление события static void run( ) // процесс с генерацией событий

{

Console.WriteLine("Для выхода нажмите Ctrl+C!"); while (true) { // бесконечный цикл

onTime( ); // посылка сообщения о событии

System.Threading.Thread.Sleep(1000); // задержка на 1 сек.

}

}

static void Main( ) {

onTime += new TimeHandler(one); // подписка на событие...

onTime += new TimeHandler(two); // для метода two run(); // запуск процесса

}

static void one( ) { // приемник сообщения string newTime = DateTime.Now.ToString(); Console.Write("\r\t\t{0}", newTime);

}

static int count = 0;

static void two( ) { // приемник сообщения Console.Write("\r{0}", count++);

}

}

Результат выполнения программы на 7-й секунде:

Для выхода нажмите Ctrl+C!

716.05.2009 10:27:17

Втексте программы обратим внимание на вывод предупреждения пользователю:

Console.WriteLine("Для выхода нажмите Ctrl+C!");

Сочетание клавиш Ctrl и C приводит к незамедлительному прекращению выполнения программы. Чтобы не "затемнять" основную схему программы,

иллюстрирующей только механизм работы с событиями, в нее не введены никакие средства диалога с пользователем.

В строке-аргументе метода консольного вывода Write() управляющая эскейп-

последовательность \r обеспечивает при каждом обращении переход в начало строки дисплея. Тем самым вывод все время выполняется в одну и ту же строку,

изображение на которой обновляется ежесекундно.

В методе one() используется свойство Now "свойство: Now" класса Data.Time

"класс: Data.Time" \y "класс" . Его назначение – вернуть текущее значение даты и времени. Применение метода ToString() позволяет представить эти значения в виде одной строки, которая затем выводится на дисплей.

Для подсчета событий (секунд) определена статическая переменная int count.

Ее значение выводит и затем увеличивает на 1 метод two().

В рассмотренном примере делегат объявлен вне классов и все методы статические – генерацию событий выполняет статический метод run(), подписаны на события два других статических метода. Таким образом, с помощью механизма событий взаимодействуют не объекты, а методы одного класса test_cs. Кроме того,

в объявлении делегата отсутствуют параметры. Поэтому при посылке сообщения о событии методы обработки не получают никакой информации из точки возникновения события.

Более общий случай – событие создаётся объектом, а в других объектах (в

объектах других классов) имеется возможность реагировать на эти события. Как мы уже показали, к одному событию может быть "приписано" несколько обработчиков и все они будут вызваны при наступлении события.

Механизм работы с событиями предусматривает несколько этапов.

Объявление делегата-типа, задающего сигнатуру тех (ещё неизвестных на данном этапе) методов, которые будут вызываться при обработке события;

Определение переменной события, имеющей тип делегата события;

Определения генератора события (посылки сообщения), с указанием аргументов, информирующих получателей о состоянии объекта,

пославшего сообщение;

Определение методов обработки события. Сигнатура каждого метода должна соответствовать типу делегата события;

Создание экземпляра того делегата, на который "настроено" событие.

Аргумент конструктора – имя метода обработки;

Подключение экземпляра делегата к переменной события.

Перечисленные этапы обычно относятся к разным классам программы. И этих

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

нём реализуются этапы 4, 5, 6. Второй класс – это класс, генерирующий события,

реализует этапы 1, 2, 3.

Зачастую в программе присутствует третий класс – управляющий процессом

на более высоком уровне. В разных задачах его называют супервизором,

монитором, диспетчером и т.п. При наличии такого класса и двух подчинённых –

класса генерации и класса обработки событий

"класс: обработки событий"

\y

"класс" - схема работы супервизора сводятся к следующим шагам:

 

Создать объект класса генерации событий

"класс: генерации событий"

\y

"класс" ;

 

 

Создать объект класса обработки событий (этого может не потребоваться, если метод обработки является статическим);

Создать экземпляр делегата, "настроив" его на метод класса обработки событий;

Подключить экземпляр делегата к переменной события из объекта класса генерации;

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

Далее всё выполняется в соответствии с общими принципами событийного управления.

В качестве примера рассмотрим программу с делегатом и четырьмя классами.

Класс Sorting содержит метод, который сортирует в порядке возрастания одномерный целочисленный массив. В процессе сортировки подсчитывается количество перестановок значений элементов. При каждом завершении внутреннего цикла формируется событие, передающее "во внешний мир" количество выполненных перестановок, размер массива и счётчик итераций внешнего цикла.

Для работы с событиями в классе объявлено событие onSort, имеющее тип

внешнего делегата SortHandler.

Класс View содержит метод обработки события. Метод выводит на консоль значение счётчика перестановок.

Класс display визуализирует динамику процесса сортировки – выводит на консоль имитацию элемента управления ProgressBar.

Метод Main() управляющего класса Controller в соответствии с общей схемой создаёт объект класса, генерирующего события, создаёт объект класса – обработчика (View). Затем подключает к переменной события два наблюдателя – два безымянных экземпляра делегата SortHandler. И, наконец, управление передаётся методу сортировки объекта – генератора событий.

//17_09.cs - события и сортировка using System;

using System.Text;

//Объявление делегата-типа:

public delegate void SortHandler(long cn, int si, int kl); class Sorting // класс сортировки массивов

{

int size; // размер массива int[ ] ar; // ссылка на массив

public long count; // счетчик обменов при сортировке public event SortHandler onSort; // объявление события public Sorting(int[ ] ls) // конструктор

{

size = ls.Length; count = 0;

ar = ls;

}

public void sort( ) // сортировка с посылкой извещений

{

int temp;

for (int i = 0; i < size - 1; i++)

{

for (int j = i + 1; j < size; j++) if (ar[i] > ar[j])

{

temp = ar[i]; ar[i] = ar[j]; ar[j] = temp; count++;

}

if (onSort != null)

onSort(count, size, i); // генерация события

}

}

}

class View

{ // Обработчик событий в объектах: public void nShow(long n, int si, int kl)

{

Console.Write("\r" + n);

}

}

class Display // Обработчик событий в этом классе

{

static int len=30; static string st = null;

public static void barShow(long n, int si, int kl)

{

int pos = Math.Abs((int)((double)kl / si * len)); string s1 = new string('\u258c', pos);

string s2 = new string('-', len - pos-1) + '\u25c4'; // unicode для треугольника;

st = s1 + '\u258c' + s2; //'\u258c' - код прямоугольника Console.Write("\r\t\t" + st);

}

}

class Controller

{

static void Main()

{

Random ran = new Random(55); int[ ] ar = new int[19999];

for (int i = 0; i < ar.Length; i++) ar[i] = ran.Next( );

Sorting run = new Sorting(ar);

View watch = new View( ); // Создан объект run.onSort += new SortHandler(Display.barShow); run.onSort += new SortHandler(watch.nShow); run.sort( );

Console.Write("\n");

}

}

1

Результат выполнения программы:

7

2

6

1

0

0

0

3

Соседние файлы в папке CSBasicCourse2ndedPodbelsky