Добавить в цитаты Настройки чтения

Страница 327 из 371

      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)

         {

             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. События — поля или процедуры-свойства? Динамическое связывание событий с их обработчиками.