Книги онлайн и без регистрации » Разная литература » Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 371 372 373 374 375 376 377 378 379 ... 415
Перейти на страницу:
class="p1">      }

     //Обработчик события — выдает сообщение.

     //Разрешает добавление элементов, меньших 10.

     private void ListChanged(object sender,

          ChangedEventArgs args)

      {

          Console.WriteLine("EventReceiveri: Сообщаю об изменениях: " + "Item ={0}", args.Item);

              args.Permit = ((int)args.Item < 10);

      }

      public void OnConnect ()

      {

           //Присоединяет обработчик к событию

           List.Changed += new ChangedEventHandler(ListChanged);

      }

       public void OffConnectO {

      {

          //Отсоединяет обработчик от события и удаляет список

          List.Changed — = new ChangedEventHandler(ListChanged);

          List = null;

      }

}//class EventReceiver1

Дам краткие комментарии.

• Среди закрытых свойств класса есть ссылка List на объект, создающий события.

• Конструктору класса передается фактический объект, который и будет присоединен к List, в конструкторе же происходит присоединение обработчика события к событию. Для этого, как положено, используется созданный в классе метод OnConnect.

• Класс содержит метод OffConnect, позволяющий при необходимости отключить обработчик от события.

• Обработчик события, анализируя переданный ему входной аргумент события Item, разрешает или не разрешает изменение элемента, формируя значение выходного аргумента Permit. Параллельно обработчик выводит на консоль сообщение о своей работе.

Класс Receiver2 устроен аналогично. Приведу его текст уже без всяких комментариев:

class Receiver2

{

     private ListWithChangedEvent List;

     public Receiver2(ListWithChangedEvent list)

     {

        List = list;

        // Присоединяет обработчик к событию.

        OnConnect ();

      }

      // Обработчик события — выдает сообщение.

      //Разрешает добавление элементов, меньших 20.

      private void ListChanged(object sender,

          ChangedEventArgs args)

      {

          Console.WriteLine("Receiver2: Сообщаю об изменениях:"

              + " Объект класса {0}: " + "Item ={1}",

                sender.GetType(), args.Item);

          args.Permit = ((int)args.Item < 20);

      }

      public void OnConnect ()

      {

          //Присоединяет обработчик к событию

          List.Changed += new ChangedEventHandler(ListChanged);

          //Заметьте, допустимо только присоединение (+=),

          //но не замена (=)

          //List.Changed = new ChangedEventHandler(ListChanged);

      }

      public void OffConnect()

      {

           //Отсоединяет обработчик от события и удаляет список

           List.Changed — = new ChangedEventHandler(ListChanged);

           List = null;

      }

}//class Receiver2

Классы созданы, теперь осталось создать объекты и заставить их взаимодействовать, чтобы одни создавали события, а другие их обрабатывали. Эту часть работы будет выполнять тестирующая процедура класса Testing:

public void TestChangeList ()

{

    //Создаются два объекта, вырабатывающие события

    ListWithChangedEvent list = new ListWithChangedEvent ();

    ListWithChangedEvent list1 = new ListWithChangedEvent();

    //Создаются три объекта двух классов EventReceiver1 и

    //Receiver2, способные обрабатывать события класса

    //ListWithChangedEvent

    EventReceiver1 Receiver1 = new EventReceiver1(list);

    Receiver2 Receiver21 = new Receiver2 (list);

    Receiver2 Receiver22 = new Receiver2(listl);

    Random rnd = new Random();

    //Работа с объектами, приводящая к появлению событий

    list.Add(rnd.Next(20)); list.Add(rnd.Next(2 0));

    list[1] =17;

    int val = (int)list[0] + (int)list[1];list.Add(val);

    list.Clear();

    list1.Add(10); list1[0] = 25; list1.Clear();

    //Отсоединение обработчика событий

    Receiver1.OffConnect();

    list.Add(21); list.Clear ();

}

В заключение взгляните на результаты работы этой процедуры.

Рис. 21.2. События в мире объектов

Две проблемы с обработчиками событий

Объекты, создающие события, ничего не знают об объектах, обрабатывающих эти события. Объекты, обрабатывающие события, ничего не знают друг о друге, независимо выполняя свою работу. В такой модели могут возникать определенные проблемы. Рассмотрим некоторые из них.

Игнорирование коллег

Задумывались ли вы, какую роль играет ключевое слово event, появляющееся при объявлении события? Событие, объявленное в классе, представляет экземпляр делегата. В предыдущей лекции, когда речь шла о делегатах, их экземпляры объявлялись без всяких дополнительных ключевых слов.

Слово "event" играет важную роль, позволяя решить проблему, названную нами "игнорированием коллег". В чем ее суть? В том, что некоторые из классов Receiver могут вести себя некорректно по отношению к своим коллегам, занимающимся обработкой того же события. При присоединении обработчика события в классе Receiver можно попытаться вместо присоединения обработчика выполнить операцию присваивания, игнорируя, тем самым, уже присоединенный список обработчиков. Взгляните еще раз на процедуру OnConnect класса Receiver2; там демонстрируется такая попытка в закомментированном операторе. Аналогично, в процедуре OffConnect вместо отсоединения (операции —) можно попытаться присвоить событию значение null, отсоединяя тем самым всех других обработчиков.

С этим как-то нужно бороться. Ключевое слово "event" дает указание компилятору создать для события закрытое поле, доступ к которому можно получить только через два автоматически создаваемых для события метода: Add, выполняющий операцию присоединения и Remove, выполняющий обратную операцию отсоединения "-=". Никаких других операций над событиями выполнять нельзя. Тем самым, к счастью, решается проблема игнорирования коллег. Ошибки некорректного поведения класса Receiver ловятся еще на этапе трансляции.

Переопределение значений аргументов события

Обработчику события, как правило, передаются входные и выходные аргументы, характеризующие событие. Они необходимы, чтобы обработчик мог нужным образом обработать событие. Но работа с аргументами требует аккуратного с ними обращения. Могут возникать проблемы, связанные с тем, что обработчик может переопределить значения аргументов в процессе своей работы.

Приведенный выше пример "Работа со списками" демонстрирует не самый лучший способ определения аргументов, провоцирующий классы Receiver на некорректное обращение с аргументами. Напомню, в классе ChangedEventArgs, определяющем аргументы события, оба свойства Item и Permit являются закрытыми. Но определены процедуры — свойства Item и Permit, реализующие полный доступ к свойствам, поскольку определены обе процедуры get и set. Это несколько облегчило задачу, поскольку позволило изменять значение входного аргумента item перед зажиганием события для передачи его обработчику. Но входной аргумент оказался не защищенным, и обработчик события может не только использовать это значение для анализа, но и изменить его в качестве побочного эффекта своей работы. В этом случае другой обработчик будет работать уже с некорректным значением. Что еще хуже — это измененное значение может использовать и класс, в процессе своей дальнейшей работы. Поэтому входные аргументы события должны быть закрытыми для обработчиков событий. Это нетрудно сделать, и я приведу необходимые уточнения.

• В классе ChangedEventArgs следует изменить процедуру-свойство Item, удалив процедуру Set, разрешающую изменение

1 ... 371 372 373 374 375 376 377 378 379 ... 415
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. В коментария нецензурная лексика и оскорбления ЗАПРЕЩЕНЫ! Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?