Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
Рис. 20.6. Службы города
Пример "Плохая служба"
Как быть, если в списке вызовов есть "плохой" экземпляр, при вызове которого возникает ошибка, приводящая к выбрасыванию исключительной ситуации? Тогда стоящие за ним в очереди экземпляры не будут вызваны, хотя они вполне могли бы выполнить свою часть работы. В этом случае полезно использовать метод GetInvocationList и в цикле поочередно вызывать делегатов. Вызов делегата следует поместить в охраняемый блок, тогда при возникновении исключительной ситуации в обработчике ситуации можно получить
Добавим в класс Combination "плохого" кандидата, который пытается делить на ноль:
//метод, вызывающий исключительную ситуацию
public static void BadService(string mes)
{
int i =7, j=5, k=0;
Console.WriteLine("Bad Service: Zero Divide");
j = i / k;
}
Создадим процедуру, в которой в списке вызовов есть хорошие и плохие кандидаты. Эта процедура использует управление исключительными ситуациями, о которых подробнее будет рассказано в последующих лекциях.
public void TestBadJob
{
MesToPers Comb;
Comb = (MesToPers)Delegate.Combine(Combination.Ambulanceman,
Combination.Policeman);
Comb = (MesToPers)Delegate.Combine(Comb,
new MesToPers(Combination.BadService));
Comb = (MesToPers)Delegate.Combine(Comb,Combination.Fireman);
foreach(MesToPers currentJob in Comb.GetinvocationList)
{
try
{
currentJob("Пожар!");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(currentJob.Method.Name);
}
}
}//BadJob
Поясню, как будет работать эта процедура при ее вызове. Вначале две службы нормально отработают, но при вызове третьей службы возникнет исключительная ситуация "деление на ноль". Универсальный обработчик Exception перехватит эту ситуацию и напечатает как свойство Message объекта е, так и имя метода, вызвавшего исключительную ситуацию, используя свойство Method объекта, вызвавшего ситуацию. После завершения работы блока обработчика ситуации выполнение программы продолжится, выполнится следующий шаг цикла, и служба пожарных благополучно выполнит свою работу. Вот результаты вывода:
Рис. 20.7. "Плохая служба"
Разговор о делегатах еще не закончен. Он будет продолжен в следующей лекции, в которой рассмотрим классы с событиями. События основываются на делегатах.
21. События
Классы с событиями. Общий взгляд. Класс Sender и классы Receivers. Класс Sender. Как объявляются события? Делегаты и события. Классы с событиями, допускаемые. Net Framework. Класс EventArgs и его потомки. Входные и выходные аргументы события. Класс Receiver. Обработчик события. Встраивание объекта Sender. Связывание обработчика с событием. Отключение обработчика. Взаимодействие объектов sender и receiver. События — поля или процедуры-свойства? Динамическое связывание событий с их обработчиками.
Классы с событиями
Каждый объект является экземпляром некоторого класса. Класс задает свойства и поведение своих экземпляров. Методы класса определяют поведение объектов, свойства — их состояние. Все объекты обладают одними и теми же методами и, следовательно, ведут себя одинаково. Можно полагать, что методы задают врожденное поведение объектов. Этого нельзя сказать о свойствах — значения свойств
Есть еще один механизм, позволяющий объектам вести себя по-разному в одних и тех же обстоятельствах. Это механизм событий, рассмотрением которого мы сейчас и займемся. Класс, помимо свойств и методов, может иметь события. Содержательно, событием является некоторое специальное состояние, в котором может оказаться объект класса. Так, для объектов класса "человек" событием может быть рождение или смерть, свадьба или развод. О событиях в мире программных объектов чаще всего говорят в связи с интерфейсными объектами, у которых события возникают по причине действий пользователя. Так, командная кнопка может быть нажата — событие Click, документ может быть закрыт — событие close, в список может быть добавлен новый элемент — событие changed.
Интерфейсные и многие другие программные объекты обладают стандартным набором предопределенных событий. В конце этой лекции мы поговорим немного об особенностях работы с событиями таких объектов. Сейчас же наше внимание будет сосредоточено на классах, создаваемых программистом. Давайте разберемся, как для таких классов создаются и обрабатываются события. Класс, решивший иметь события, должен уметь, по крайней мере, три вещи:
• объявить событие в классе;
• зажечь в нужный момент событие, передав обработчику необходимые для обработки аргументы. (Под зажиганием или включением события понимается некоторый механизм, позволяющий объекту уведомить клиентов класса, что у него произошло событие.);
• проанализировать, при необходимости, результаты события, используя значения выходных аргументов события, возвращенные обработчиком.
Заметьте, что, зажигая событие, класс посылает сообщение получателям события — объектам некоторых других классов. Будем называть класс, зажигающий событие, классом — отправителем сообщения (sender). Класс, чьи объекты получают сообщения, будем называть классом — получателем сообщения (receiver). Класс-отправитель сообщения, в принципе, не знает своих получателей. Он отправляет сообщение в межмодульное пространство. Одно и то же сообщение может быть получено и по-разному обработано произвольным числом объектов разных классов. Взгляните на схему, демонстрирующую взаимодействие объектов при посылке и получении сообщения.
Рис. 21.1. Взаимодействие объектов. Посылка и получение сообщения о событии
Класс sender. Как объявляются события?
При проектировании класса с событиями, возможно, самое трудное — содержательная сторона дела. Какими событиями должен обладать класс, в каких методах и в какой момент зажигать то или иное событие?
Содержательную сторону будем пояснять на содержательных примерах. А сейчас рассмотрим технический вопрос: как объявляются события средствами языка С#? Прежде всего, уточним, что такое событие с программистской точки зрения. Начнем не с самого события, а с его обработчика.