|
Прошел курс. Получил код 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 - простой) и требует минимальных ресурсов компьютера. Только консольные приложения, многие из которых называют утилитами, выполняются непрерывно от начала до конца, и возможно не требуют применения событий так остро. Но и они для обмена информацией объектов приложения тоже могут использовать механизм событий, в чем мы только что убедились на последнем примере.


