Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет: Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.
Как активировать код? |
Интерфейсы, делегаты, события в C#
Создание событий с контролем адресатов
Если мы только используем библиотечные классы, содержащие события, то нам вообще никакой способ создания событий знать необязатально. Уметь подписывать в клиенте обработчики на уже готовые события и понимать причины, приводящие к возникновению этих событий, - это все, что нам необходимо.
Если мы хотим как-то повлиять на событие библиотечного класса, то нужно помнить, что событие объявляется общедоступными и может наследоваться как обычный член класса. Это дает нам право расширить библиотечный класс, в котором можно выполнить какие-то действия: скрыть событие, объявив его заново с ключевым словом new как закрытое, или переопределить диспетчер события. Диспетчер в библиотечных классах обычно объявляется виртуальным и может быть переопределен в производном классе или даже совсем скрыт для следующих потомков его переобъявлением.
Независимо от того, создаем ли мы свои классы с событиями, или расширяем библиотечные классы, добавляя в них новые события, о существовании еще одного способа знать не помешает.
Рассмотренный ранее способ создания объявления событий в классах-источниках считается стандартным. Мы создали таким способом класс с событием и теперь этим событием может пользоваться кто угодно, присоединяя к нему любые обработчики, лишь бы они имели разрешенный прототип. Такой способ не позволяет программисту осуществлять должный контроль за обработчиками при их добавлении и изъятии, как это может потребоваться в некоторых случаях. Для решения подобных задач существует другой способ создания события в классе-источнике, который является более гибким и его называют расширенным. Этот способ позволяет отслеживать в классе, содержащем событие, те обработчики, которые клиентский код будет присоединять к нему.
Синтаксис создания события расширенным способом сильно напоминает создание в классе свойства. Он также основывается на закрытом базовом поле и также обертывает это поле общедоступным членом. Только вместо автоматически вызываемых аксессоров get и set используются ключевые слова add и remove, а вместо базового поля используется закрытое поле-делегат. Методы add и remove автоматически вызываются при добавлении обработчика в список делегата или удаления его из списка.
Пусть мы создаем класс с событием и не хотим, чтобы какой-то определенный клиентский класс пользовался этим событием, т.е. один тип не хочет обслуживать другой неприятный ему тип. Тогда код создания события мог бы выглядеть так, как показано в следующем примере
using System; namespace Test { // Объявление делегата как типа delegate void Message(object sender, string message); // Класс-источник сообщения class SourceMessage { //////////////////////////////////////////// // Это был стандартный способ //////////////////////////////////////////// // public event Message Mail; //////////////////////////////////////////// // Это расширенный способ //////////////////////////////////////////// // Объявление внутреннего поля как экземпляра делегата private Message mail; // Создание события с контролем адресатов public event Message Mail { add // Контролируем добавление обработчиков в список { // Имя получателя сообщения string targetName = value.Target.GetType().Name; // Выявляем того, кого не любим! if (targetName == "BadClass") // Ласково уведомляем Console.WriteLine("Типу BadClass доступ к событию запрещен!\n"); else mail += value; } remove // Удаление контролировать не будем. //"Его там не стояло!" { mail -= value; } } ///////////////////////////////////////////// // Все остальное то-же самое, кроме замены // события Mail на закрытое поле-делегат mail ///////////////////////////////////////////// // Метод диспетчеризации события. Объявили виртуальным // и защищенным для возможности замещения в наследниках protected virtual void OnMail(string mess) { // Инициируем рассылку сообщения всем, // кто подписался на событие // Здесь используется поле-делегат, внутри можно (и нужно!) if (mail != null) // Если не пустой делегат mail(this, mess); // Инициируем событие } // Объявляем и инициируем внутреннее поле базовым сообщением string message = "Сообщаю, что сработал таймер!!!\n" + "Текущее время "; // Объявляем внутреннее поле для видимости в методах System.Timers.Timer timer; // Конструктор класса-источника сообщения public SourceMessage() { // Создаем и запускаем таймер, который по истечении // заданного времени инициирует рассылку сообщения timer = new System.Timers.Timer(); timer.Interval = 5000d; // Сработает через 5 секунд timer.Elapsed += new System.Timers.ElapsedEventHandler (timer_Elapsed); timer.Start(); // Запускаем таймер } // Инициирование события из внешнего источника void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { // Извлекаем текущее системное время DateTime curTime = DateTime.Now; // Дополняем базовое сообщение временем message += curTime.ToString(); OnMail(message); // Вызываем метод диспетчеризации timer.Stop(); // Останавливаем системный таймер } } // Хороший класс-получатель сообщения class GoodClass { public GoodClass() { // Создаем объект с событием SourceMessage obj = new SourceMessage(); // Подписываем на событие обработчик, // печатающий полученную информацию obj.Mail += new Message(obj_Mail); } // Обработчик void obj_Mail(object sender, string message) { String str = this.GetType().Name + " получил информацию от " + sender.GetType().Name + "\n" + "следующего содержания:\n\"" + message + "\""; Console.WriteLine(str); } } // Плохой класс, которому не разрешено получать сообщения class BadClass { public BadClass() { // Создаем объект с событием SourceMessage obj = new SourceMessage(); // Подписываемся на событие в благостном неведении, // что мы попали в черный список и нас // не включат в список рассылки obj.Mail += new Message(obj_Mail); } // Обработчик void obj_Mail(object sender, string message) { String str = "Получена информация от класса " + sender.GetType().Name + ":\n" + "\"" + message + "\""; Console.WriteLine(str); } } // Запуск class Program { static void Main() { // Настройка консоли Console.Title = "Расширенное создание события (через 5 секунд!)"; Console.ForegroundColor = ConsoleColor.White; Console.CursorVisible = false; Console.WindowWidth = 47; Console.WindowHeight = 7; new GoodClass();// Исполняем хороший класс new BadClass(); // Исполняем плохой класс Console.ReadLine(); } } }Листинг 10.20 . Создание события с контролирующим подписку кодом
Из распечатки мы видим, что хотя событие класса GoodClass и было инициировано первым, но отработало позднее, поскольку ждало срабатывания системного таймера.
Создание событий со списком делегатов
В библиотеке .NET Framework для структурированной поддержки событий создан класс EventHandlerList, который может хранить в себе элементы, выполняющие функцию делегатов с присоединенными к ним обработчиками. Эти элементы маркируются ключами типа Object. Каждому ключу соответствует один или несколько одноадресных делегатов, способных хранить вызовы обработчиков одного и того же прототипа.
За событием закрепляется определенный ключ и при манипуляции с событием во внешнем коде делегаты, маркированные этим ключом, добавляются в список. Чтобы активизировать событие, достаточно обратиться к списку с закрепленным за событием ключом, все одноадресные делегаты будут извлечены из списка и присоединенные обработчики будут выполнены.
Поскольку элементы списка реализуют вызов обработчиков одноадресным способом, то использование списка позволяет прикреплять обработчики не только с пустым возвращаемым значением, но и с возвращаемым значением любого типа. Хотя в большинстве случаев это несущественное преимущество, поскольку результат обработки можно вернуть и через аргументы обработчика. Класс EventHandlerList находится в пространстве имен System. ComponentModel. В следующем примере демонстрируется создание событий на основе списка одноадресных делегатов.
using System; namespace Test { // Класс-источник сообщения class SourceMessage { // Создание списка делегатов вместо базовых полей System.ComponentModel.EventHandlerList eventList = new System.ComponentModel.EventHandlerList(); // Объявление типов делегатов public delegate void Message1(); public delegate int Message2(string message); // Непустое возвращаемое значение public delegate void Message3(object sender, string message); // Создание ключей для делегатов Object key1 = new Object(); Object key2 = new Object(); Object key3 = new Object(); // Создание события на базе списка public event Message1 Mail1 { add { eventList.AddHandler(key1, value);// Дополняем список делегатов } remove { eventList.RemoveHandler(key1, value);// Удаляем из списка } } // Создание события на базе списка public event Message2 Mail2 { add { eventList.AddHandler(key2, value);// Расширяем список делегатов } remove { eventList.RemoveHandler(key2, value);// Удаляем из списка } } // Создание события на базе списка public event Message3 Mail3 { add { eventList.AddHandler(key3, value);// Расширяем список делегатов } remove { eventList.RemoveHandler(key3, value);// Удаляем из списка } } // Симуляция срабатывания события Mail1 public void DispatchMail1() { // Извекаем из списка все делегаты для Mail1, помеченные ключом Message1 mail1 = (Message1)eventList[key1]; if (mail1 != null) mail1(); } // Симуляция срабатывания события Mail2 public void DispatchMail2() { // Извекаем из списка все делегаты для Mail2, помеченные ключом Message2 mail2 = (Message2)eventList[key2]; if (mail2 != null) mail2("\"mail2 из SourceMessage\""); } // Симуляция срабатывания события Mail3 public void DispatchMail3() { // Извекаем из списка все делегаты для Mail3, помеченные ключом Message3 mail3 = (Message3)eventList[key3]; if (mail3 != null) mail3(this, "\"mail3 из SourceMessage\""); } } // Получатель сообщения class MyClass { // Конструктор public MyClass() { // Создаем объект с событиями SourceMessage obj = new SourceMessage(); // Подписываемся на обработчики obj.Mail1 += new SourceMessage.Message1(obj_Mail1); obj.Mail1 += new SourceMessage.Message1(obj_Mail1); obj.Mail2 += new SourceMessage.Message2(obj_Mail2); obj.Mail3 += new SourceMessage.Message3(obj_Mail3); // Запускаем события obj.DispatchMail1(); obj.DispatchMail2(); obj.DispatchMail3(); } // Обработчики void obj_Mail1() { Console.WriteLine("Обработчик события Mail1."); } int obj_Mail2(string message) { Console.WriteLine("\nОбработчик события Mail2.\n" + "Сообщение: {0}\n", message); return 1; } void obj_Mail3(object sender, string message) { Console.WriteLine("Обработчик события Mail3.\n" + "Сообщение: {0}", message); } } // Запуск class Program { static void Main() { // Настройка консоли Console.Title = "Применение списка делегатов"; Console.ForegroundColor = ConsoleColor.White; Console.CursorVisible = false; Console.WindowWidth = 36; Console.WindowHeight = 9; new MyClass();// Исполняем Console.ReadLine(); } } }Листинг 10.21 . Создание событий со списком делегатов