Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет: Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.
Как активировать код? |
Интерфейсы, делегаты, события в C#
Превращение делегата в событие
А надо бы их различить, поскольку они предназначены совершенного для разных целей и имеют разные механизмы обработки. Чтобы внести ясность в этот вопрос, разработчики C# и библиотеки .NET Framework решили объявлять ссылку на объект делегата, когда он используеся для рассылки сообщений, с ключевым словом event и называть ее событием.
Остальной механизм работы такого делегата-события остался прежним, кроме одного: теперь событие-делегат является собственностью класса-источника и вызывать его напрямую из класса-приемника как поле сообщений нельзя. Событие должно инициироваться функцией-членом класса, отсылающего сообщение ( функция диспетчеризации ), в котором оно для этих целей и объявлено. В приемнике сообщения можно только подписаться на событие источника путем прикрепления нужных функций-обработчиков, а далее вызвать функцию диспетчеризации владельца события. Но по-прежнему наполнение списка события конкретными адресами функций выполняют объекты-делегаты.
Объявление в классе-источнике делегата как события означает только возможность на него подписаться функциям внешнего кода, но не гарантирует, что на него действительно кто-то подпишется в момент возбуждения этого события в классе-источнике. Поэтому функция диспетчеризации вначале проверяет, не пустой ли делегат, и только потом выполняет вызов связанных с ним методов-обработчиков.
В соответствии со сказанным изменим предыдущий пример совсем на немного (выделено в коде) и получим прежний результат, только теперь уже с участием события как члена класса
using System; namespace Test { // Образец сообщения определяется делегатом delegate void Message(string message); // Источник сообщения class SourceMessage { // Общедоступное поле ссылки на объект-делегат, // который теперь называется событием и наполняется // указателями на функции в классах-получателях public event Message mail; // Необязательное поле с рассылаемым сообщение public string message; // Разослать сообщение - функция диспетчеризации public void DispatchMessage(string mess) { // Сохраняем внешнее сообщение во внутреннем поле message = mess; // Инициируем рассылку сообщения всем, // кто зарегистрировался в объекте-делегате if (mail != null) // Если не пустой делегат mail(mess); } } // Получатель сообщения class Addressee1 { // Функции public void Handler(string message) { Console.WriteLine("Addressee1 получил:" + "\n\t\"{0}\"", message); } } // Получатель сообщения class Addressee2 { // Функции public void Handler(string message) { Console.WriteLine("Addressee2 получил:" + "\n\t\"{0}\"", message); } } // Вызывающая сторона class MyClass { static public string Title = "Рассылка сообщений событием"; public MyClass() { // Создаем объекты источника и получателей сообщения SourceMessage source = new SourceMessage(); Addressee1 obj1 = new Addressee1(); Addressee2 obj2 = new Addressee2(); // Формируем список обработчиков события с //помощью объектов-делегатов source.mail += new Message(obj1.Handler); source.mail += new Message(obj2.Handler); // Рассылаем сообщение только через функцию-член источника //source.mail("Первое сообщение"); source.DispatchMessage("Первое сообщение"); Console.WriteLine(); // Рассылаем сообщение через функцию диспетчеризации source.DispatchMessage("Второе сообщение"); } } // Запуск class Program { static void Main() { // Настройка консоли Console.Title = MyClass.Title; Console.ForegroundColor = ConsoleColor.White; Console.CursorVisible = false; Console.WindowWidth = 32; Console.WindowHeight = 10; new MyClass();// Исполняем Console.ReadLine(); } } }Листинг 10.18 . Однонаправленная передача сообщений объектам с помощью события
Если теперь посмотреть на состав класса SourceMessage через панель Class View, то мы можем отличить переменную от события.
В последнем примере событие объявлено в одном классе, подписка обработчиков выполнена в другом классе, а сами обработчики находятся в третьих двух классах. Но, как правило, обработчики принадлежат тому классу, который содержит в себе экземпляр класса-источника события и в котором выполнена подписка на событие. Кроме того, инициация события выполняется внешними причинами в результате действий пользователя или возникновения каких-то программных условий. Чаще всего событие инициируется самой операционной системой. В нашем случае мы имитируем внешнюю причину, используя для этого нашу функцию диспетчеризации DispatchMessage().
Когда вместо объекта-делегата для передачи сообщений используется объект-событие, то включенные в список события функции называют обработчиками. Многие клиенты объекта с событием могут подписаться на его обработку. Тогда при возникновении события обработчики клиентов будут выполняться в том порядке, в котором они следуют в списке события. Как и в случае с делегатами, сбой любого обработчика прервет выполнение оставшихся членов списка события.
Типичный способ создания событий
В соответствии с парадигмой объектно-ориентированного программирования событие должно передаваться объектом-источником при возникновении определенных обстоятельств. Другие классы, содержащие в себе объект с общедоступным полем-событием, по отношению к этому событию могут выступать только как клиенты (внешний код). Они могут только подписаться традиционным способом на событие и прослушивать его, а при получении - реагировать кодом прикрепленных к событию своих обработчиков. Согласуем код нашего последнего примера с изложенными соображениями
using System; namespace Test { // Образец сообщения определяется делегатом delegate void Message(object sender, string message); // Класс-источник сообщения class SourceMessage { // Общедоступное поле ссылки на событие public event Message 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 MyClass { static public string Title = "Передача сообщения событием (через 5 секунд!)"; public MyClass() { // Создаем объект с событием 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 = MyClass.Title; Console.ForegroundColor = ConsoleColor.White; Console.CursorVisible = false; Console.WindowWidth = 45; Console.WindowHeight = 4; new MyClass();// Исполняем Console.ReadLine(); } } }Листинг 10.19 . Пример типичного создания событий
В последнем примере в классе-источнике сообщения мы объявили поле-ссылку на событие как общедоступную, чтобы в любом классе-клиенте, содержащем объект с событием, эта ссылка была видна и позволяла подписать обработчики. Для инициации события мы в качестве внешней причины применили срабатывание системного таймера. В реальных условиях это может быть что угодно, но чаще всего события генерируются операционной системы, как реакция на действия пользователя или как ответ на переход программных объектов в определенные состояния.
Модель программирования, основанная на событиях, сейчас является наиболее популярной, а для некоторых задач - и единственно возможной. Она наиболее приспособлена для построения интерактивных приложений, где требуется организовать диалог с пользователем. Когда пользователь не производит никаких действий, приложение находится в состоянии простоя ( idle - простой) и требует минимальных ресурсов компьютера. Только консольные приложения, многие из которых называют утилитами, выполняются непрерывно от начала до конца, и возможно не требуют применения событий так остро. Но и они для обмена информацией объектов приложения тоже могут использовать механизм событий, в чем мы только что убедились на последнем примере.