Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
Начнем с построения класса с именем Combination, где, следуя уже описанной технологии, введем делегатов как закрытые свойства, доступ к которым идет через процедуру-свойство get. Три делегата одного класса будут описывать действия трех городских служб. Класс будет описываться ранее введенным делегатом MesToPers, размещенным в пространстве имен проекта. Вот программный код, в котором описаны функции, задающие действия служб:
class Combination
{
private static void policeman (string mes)
{
//анализ
if(mes =="Пожар!")
Console.WriteLine(mes + " Милиция ищет виновных!");
else
Console.WriteLine(mes +" Милиция здесь!");
}
private static void ambulanceman(string mes)
{
if(mes =="Пожар!")
Console.WriteLine(mes + " Скорая спасает пострадавших!");
else
Console.WriteLine(mes + " Скорая помощь здесь!");
}
private static void fireman(string mes)
{
if(mes =="Пожар!")
Console.WriteLine(mes + " Пожарные тушат пожар!");
else
Console.WriteLine(mes + " Пожарные здесь!");
}
}
Как видите, все три функции имеют не только одинаковую сигнатуру, но и устроены одинаково. Они анализируют приходящее к ним сообщение, переданное через параметр mes, а затем, в зависимости от результата, выполняют ту или иную работу, которая в данном случае сводится к выдаче соответствующего сообщения. Сами функции закрыты, и мы сейчас организуем к ним доступ:
public static MesToPers Policeman
{
get {return (new MesToPers(policeman));}
}
public static MesToPers Fireman
{
get {return (new MesToPers(fireman));}
}
public static MesToPers Ambulanceman
(
get (return (new MesToPers(ambulanceman));}
}
Три статических открытых свойства — Policeman, Fireman, Ambulanceman — динамически создают экземпляры класса MesToPers, связанные с соответствующими закрытыми функциями класса.
Службы у нас есть, покажем, как с ними можно работать. С этой целью добавим в класс Testing, где проводятся различные эксперименты, следующую процедуру:
public void TestSomeServices
{
MesToPers Comb;
Comb = (MesToPers)Delegate.Combine(Combination.Ambulanceman,
Combination.Policeman);
Comb = (MesToPers)Delegate.Combine(Comb,Combination.Fireman);
Comb("Пожар!");
Вначале объявляется без инициализации функциональная переменная Comb, которой в следующем операторе присваивается ссылка на экземпляр делегата, созданного методом Combine, чей список вызова содержит ссылки на экземпляры делегатов Ambulanceman и Policeman. Затем к списку вызовов экземпляра Comb присоединяется новый кандидат Fireman. При вызове делегата Comb ему передается сообщение "Пожар!". В результате вызова Comb поочередно запускаются все три экземпляра входящие в список, каждому из которых передается сообщение.
Давайте теперь начнем поочередно отключать делегатов, вызывая затем Comb с новыми сообщениями:
Comb = (MesToPers)Delegate.Remove (Comb,Combination.Policeman);
//Такое
возможно: попытка отключить не существующий элементComb = (MesToPers)Delegate.Remove (Comb,Combination.Policeman);
Comb ("Через 30 минут!");
Comb = (MesToPers)Delegate.Remove(Comb,Combination.Ambulanceman);
Comb("Через час! ");
Comb = (MesToPers)Delegate.Remove(Comb,Combination.Fireman);
//Comb("Через два часа!"); // Comb не определен
В этом фрагменте поочередно отключаются разные службы — милиция, скорая помощь, пожарные, и каждый раз вызывается Comb. После последнего отключения, когда список вызовов становится пустым, вызов Comb приводит к ошибке, потому оператор вызова закомментирован.
Покажем теперь, что ту же работу можно выполнить, используя не методы, а операции:
//операции + и -
Comb = Combination.Ambulanceman;
Console.WriteLine(Comb.Method.Name);
Comb+= Combination.Fireman;
Comb+= Combination.Policeman;
Соmb("День города!");
Comb — = Combination.Ambulanceman;
Comb — = Combination.Fireman;
Comb("На следующий день!");
}//TestSomeServices
Обратите внимание, здесь демонстрируется вызов свойства Method, возвращающее объект, свойство Name которого выводится на печать. Результаты, порожденные работой этой процедуры, изображены на рис. 20.6.
Рис. 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)
{