Высокоуровневые методы информатики и программирования Лекция 10 События План работы • • • • что такое и для чего используются как создаются как инициируются как обрабатываются Работа с событиями в C# • С помощью делегатов в классах можно описывать события. • События это способ взаимодействия между объектами разных классов. – Объекты одного класса могут использовать объекты других классов. – Для этого они создают объекты требуемого класса и вызывают его методы (передают ему сообщения). – При работе вызванных методов часто возникает необходимость передать сообщения о возникших ситуациях (важных изменения в своей работе) объектам вызывающего класса, без завершения их работы. – Для передачи таких сообщений и используются специальные элементы класса – события. События Сообщение объекта своему пользователю о том, что произошло что-то важное. Например: • Выполнена какая-то часть работы • Произошла ошибка • Пользователь выполнил некоторые действия (переместил мышь, сделал щелчок, нажал клавишу) Вся работа программ с среде ОС Windows основана на событиях Программирование основанное на событиях – event driven programming. События (2) • Обычно класс пользователь (client) вызывает методы используемого класса (server) • События позволяют используемому классу вызывать методы пользовательского класса Вызов методов Сервер Клиент Пользовательский класс Используемый класс Сообщения о событиях События (3) Метод, использующий MyClass MyClass a; public void MySub() { a = new OtherClass a.SomeEvent += MyHandle; … a.metod(); // вызываем метод с событиями … } Обработчик события void MyHandle(object o, int d) public delegate Handler(…); public event Handler SomeEvent; Метод класса MyClass с событием SomeEvent: Метод: method() Произошло что-то важное SomeEvent(this, data) Вызов обработчика события Возврат управления { ... } Проблема: как объект узнает о том, какую функцию у пользователя вызывать ??? Добавление события в класс 1. Создать открытую переменную, которая может хранить ссылки. 2. Когда возникнет нужное событие, то вызвать все методы, ссылки на которые этой переменной будут присвоены. Объявление события в классе • Вначале объявляется делегат, как это рассматривалось ранее. Объявление делегата описывается в некотором классе. Но, часто, это объявление находится вне класса в пространстве имен. public delegate <тип> <имя_делегата> ([параметры]); – Например: public delegate void MyHandler (object o, int n); • Объявить событие как экземпляр соответствующего делегата. При этом используется ключевое слово event (это гарантирует, что экземпляр события не может быть вызван в других классах). рublic event <имя_делегата> <имя_события>; – • Например: public event MyHandler MyEvent; Для инициирования события требуется просто вызвать на выполнение экземпляр делегата. – Например: if (MyEvent != null) // проверка, что для события заданы обработчики MyEvent(this, i); // если есть обработчики, то запускаем событие Сигнатура обработчика события • В FCL все обработчики имеют два параметра public delegate void MyChangeHandler (MyObject o, MyEventArgs i); – EventArgs – базовый класс для всех классов передачи данных о событии – class MyEventArgs : EventArgs { . . . } Объявление и инициирование события класса • Объявления делегата // делегат, который должны реализовать подписчики public delegate void Handler (object o, EventArgs ea); // объявление событий event public event Handler OnChange; public event Handler OnClick; public event Handler OnMove; Ключевое слово event Ключевое слово event указывает компилятору, что делегат может вызываться описывающим классом, и что другие классы могут только подписываться и отписываться от делегата используя соответствующие += и -=. Пример описания события // Объявление нового типа – делегата public delegate void MyHandler (object o, int n); // объявление класса с событием public class ClassWithEvent { // класс с событием // Создание экземпляра делегата - событие public event MyHandler MyEvent; // поле класса private int Volume = 0; // конструктор класса – парамер – объем работы public EventClass(int p){if (p > 0) Volume = p;} //описание метода выполняющего долгую работу (в данном примере бессмысленную) public void LongWork(){ double s = 0; int k = 0; int st = Volume / 10; // десятая часть работы for (int i = 0; i < Volume; i++) { s += Math.Atan(Math.Sqrt(i) * Math.Sqrt(i)); if (k == st) {// выполнена заданная часть работы if (MyEvent != null){ int n = (int)(i*100.0)/Volume; MyEvent(this, n); // запускаем событие } k = 0; } else k += 1; }}} Обработка событий в классах В классе, который будет обрабатывать событие, нужно: 1. описать метод - обработчик события, который имеет такую же сигнатуру, как у делегата, который задает событие; 2. в одном из методов создать объект, имеющий событие; 3. присвоить ссылку на метод – обработчик события – открытой переменной объекта (событию) с использованием операции +=. Пример обработки события class Program { const int WorkVolume = 10000000; // объем работы // обработчик события private static void ShowStar(object o, int n) { Console.WriteLine(" Выполнено {0}%", n); //Console.Write("*"); // можно просто выводить * } public static void Main() { // создается объект ClassWithEvent obj=new ClassWithEvent(WorkVolume); // событию задается обработчик obj.MyEvent += new MyHandler(ShowStar); // запускам метод объекта у которого возникает данное событие obj.LongWork(); // ждем нажатия клавиши Console.ReadLine(); } } Подписка и отписка на событие • Создание метода, который имеет такую же сигнатуру, что и обработчик события – Handler void f (….); • Создать экземпляр делегата Handler a = new Handler (func); • Подписаться на событие (obj – объект у которого есть событие) obj.OnChange += a; • Отписаться от события (obj – объект у которого есть событие) obj.OnChange -= a; Или • Подписаться на событие obj.OnChange += new Handler (func); • Отписаться от события obj.OnChange -= new Handler (func);