Комбинирование делегатов"
Рассмотрим следующую ситуацию. Пусть есть городские службы: милиция, скорая помощь, пожарные. Каждая из служб по-своему реагируют на события, происходящие в городе. Построим примитивную модель жизни города, в которой случаются события и сообщения о них посылаются службам. В последующей лекции эта модель будет развита. Сейчас она носит формальный характер, демонстрируя, главным образом, работу с делегатами, заодно поясняя ситуации, в которых разумно комбинирование делегатов.
Начнем с построения класса с именем 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; Comb("День города!"); Comb -= Combination.Ambulanceman; Comb -= Combination.Fireman; Comb("На следующий день!"); }//TestSomeServices
Обратите внимание, здесь демонстрируется вызов свойства Method, возвращающее объект, свойство Name которого выводится на печать. Результаты, порожденные работой этой процедуры, изображены на рис. 20.6.
Рис. 20.6. Службы города