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

CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky

.pdf
Скачиваний:
32
Добавлен:
22.03.2016
Размер:
2.08 Mб
Скачать

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

// 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