|
Прошел курс. Получил код 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 .
Создание событий со списком делегатов

