События сигнализируют системе о том, что произошло определенное действие. И если нам надо отследить эти действия, то как раз мы можем применять события.
Например, возьмем следующий класс, который описывает банковский счет:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Account{ public Account(int sum) { Sum = sum; } // сумма на счете public int Sum { get; private set;} // добавление средств на счет public void Put(int sum) { Sum += sum; } // списание средств со счета public void Take(int sum) { if (Sum >= sum) { Sum -= sum; } }} |
В конструкторе устанавливаем начальную сумму, которая хранится в свойстве Sum. С помощью метода Put мы можем добавить средства на счет, а с помощью метода Take, наоборот, снять деньги со счета. Попробуем использовать класс в программе - создать счет, положить и снять с него деньги:
1 2 3 4 5 6 7 8 9 10 11 | static void Main(string[] args){ Account acc = new Account(100); acc.Put(20); // добавляем на счет 20 Console.WriteLine($"Сумма на счете: {acc.Sum}"); acc.Take(70); // пытаемся снять со счета 70 Console.WriteLine($"Сумма на счете: {acc.Sum}"); acc.Take(180); // пытаемся снять со счета 180 Console.WriteLine($"Сумма на счете: {acc.Sum}"); Console.Read();} |
Все операции работают как и положено. Но что если мы хотим уведомлять пользователя о результатах его операций. Мы могли бы, например, для этого изменить метод Put следующим образом:
1 2 3 4 5 | public void Put(int sum) { Sum += sum; Console.WriteLine($"На счет поступило: {sum}");} |
Казалось, теперь мы будем извещены об операции, увидев соответствующее сообщение на консоли. Но тут есть ряд замечаний. На момент определения класса мы можем точно не знать, какое действие мы хотим произвести в методе Put в ответ на добавление денег. Это может вывод на консоль, а может быть мы захотим уведомить пользователя по email или sms. Более того мы можем создать отдельную библиотеку классов, которая будет содержать этот класс, и добавлять ее в другие проекты. И уже из этих проектов решать, какое действие должно выполняться. Возможно, мы захотим использовать класс Account в графическом приложении и выводить при добавлении на счет в графическом сообщении, а не консоль. Или нашу библиотеку классов будет использовать другой разработчик, у которого свое мнение, что именно делать при добавлении на счет. И все эти вопросы мы можем решить, используя события.
Определение и вызов событий
События объявляются в классе с помощью ключевого слова event, после которого указывается тип делегата, который представляет событие:
1 2 | delegate void AccountHandler(string message);event AccountHandler Notify; |
В данном случае вначале определяется делегат AccountHandler, который принимает один параметр типа string. Затем с помощью ключевого слова event определяется событие с именем Notify, которое представляет делегат AccountHandler. Название для события может быть произвольным, но в любом случае оно должно представлять некоторый делегат.
Определив событие, мы можем его вызвать в программе как метод, используя имя события:
1 | Notify("Произошло действие"); |
Поскольку событие Notify представляет делегат AccountHandler, который принимает один параметр типа string - строку, то при вызове события нам надо передать в него строку.
Однако при вызове событий мы можем столкнуться с тем, что событие равно null в случае, если для его не определен обработчик. Поэтому при вызове события лучше его всегда проверять на null. Например, так:
1 | if(Notify !=null) Notify("Произошло действие"); |
Или так:
1 | Notify?.Invoke("Произошло действие"); |
В этом случае поскольку событие представляет делегат, то мы можем его вызвать с помощью метода Invoke(), передав в него необходимые значения для параметров.
Объединим все вместе и создадим и вызовем событие:
class Account{ public delegate void AccountHandler(string message); public event AccountHandler Notify; // 1.Определение события public Account(int sum) { Sum = sum; } public int Sum { get; private set;} public void Put(int sum) { Sum += sum; Notify?.Invoke($"На счет поступило: {sum}"); // 2.Вызов события } public void Take(int sum) { if (Sum >= sum) { Sum -= sum; Notify?.Invoke($"Со счета снято: {sum}"); // 2.Вызов события } else { Notify?.Invoke($"Недостаточно денег на счете. Текущий баланс: {Sum}"); ; } }}С событием может быть связан один или несколько обработчиков.
Обработчики событий - это именно то, что выполняется при вызове событий.
Нередко в качестве обработчиков событий применяются методы.
Каждый обработчик событий по списку параметров и возвращаемому типу должен соответствовать делегату, который представляет событие.
Для добавления обработчика события применяется операция +=:
1 | Notify += обработчик события; |
Определим обработчики для события Notify, чтобы получить в программе нужные уведомления:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Program{ static void Main(string[] args) { Account acc = new Account(100); acc.Notify += DisplayMessage; // Добавляем обработчик для события Notify acc.Put(20); // добавляем на счет 20 Console.WriteLine($"Сумма на счете: {acc.Sum}"); acc.Take(70); // пытаемся снять со счета 70 Console.WriteLine($"Сумма на счете: {acc.Sum}"); acc.Take(180); // пытаемся снять со счета 180 Console.WriteLine($"Сумма на счете: {acc.Sum}"); Console.Read(); } private static void DisplayMessage(string message) { Console.WriteLine(message); }} |
В данном случае в качестве обработчика используется метод DisplayMessage, который соответствует по списку параметров и возвращаемому типу делегату AccountHandler. В итоге при вызове события Notify?.Invoke() будет вызываться метод DisplayMessage, которому для параметра message будет передаваться строка, которая передается в Notify?.Invoke(). В DisplayMessage просто выводим полученное от события сообщение, но можно было бы определить любую логику.
Если бы в данном случае обработчик не был бы установлен, то при вызове события Notify?.Invoke() ничего не происходило, так как событие Notify было бы равно null.
Консольный вывод программы:
На счет поступило: 20 Сумма на счете: 120 Со счета снято: 70 Сумма на счете: 50 Недостаточно денег на счете. Текущий баланс: 50 Сумма на счете: 50
Теперь мы можем выделить класс Account в отдельную библиотеку классов и добавлять в любой проект.
Добавление и удаление обработчиков
Для одного события можно установить несколько обработчиков и потом в любой момент времени их удалить. Для удаления обработчиков применяется операция -=. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class Program{ static void Main(string[] args) { Account acc = new Account(100); acc.Notify += DisplayMessage; // добавляем обработчик DisplayMessage acc.Notify += DisplayRedMessage; // добавляем обработчик DisplayMessage acc.Put(20); // добавляем на счет 20 acc.Notify -= DisplayRedMessage; // удаляем обработчик DisplayRedMessage acc.Put(20); // добавляем на счет 20 Console.Read(); } private static void DisplayMessage(string message) { Console.WriteLine(message); } private static void DisplayRedMessage(String message) { // Устанавливаем красный цвет символов Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(message); // Сбрасываем настройки цвета Console.ResetColor(); }} |
Консольный вывод:
На счет поступило: 20
На счет поступило: 20
На счет поступило: 20
В качестве обработчиков могут использоваться не только обычные методы, но также делегаты, анонимные методы и лямбда-выражения. Использование делегатов и методов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | static void Main(string[] args){ Account acc = new Account(100); // установка делегата, который указывает на метод DisplayMessage acc.Notify += new Account.AccountHandler(DisplayMessage); // установка в качестве обработчика метода DisplayMessage acc.Notify += DisplayMessage; // добавляем обработчик DisplayMessage acc.Put(20); // добавляем на счет 20 Console.Read();}private static void DisplayMessage(string message){ Console.WriteLine(message);} |
В данном случае разницы между двумя обработчиками никакой не будет.
Установка в качестве обработчика анонимного метода:
1 2 3 4 5 6 7 8 9 10 11 | static void Main(string[] args){ Account acc = new Account(100); acc.Notify += delegate (string mes) { Console.WriteLine(mes); }; acc.Put(20); Console.Read();} |
Установка в качестве обработчика лямбда-выражения:
1 2 3 4 5 6 7 8 | static void Main(string[] args){ Account acc = new Account(100); acc.Notify += mes =>Console.WriteLine(mes); acc.Put(20); Console.Read();} |
Комментариев нет:
Отправить комментарий