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

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

• индексатор this, дающий доступ к элементу списка по индексу;

• метод Clear, производящий чистку списка.

//Переопределяемые методы, вызывающие событие Changed

//Добавление нового элемента

//при получении разрешения у обработчиков события

public override int Add (object value)

{

    int i=0;

    evargs.Item = value;

    OnChanged(evargs);

    if (evargs.Permit)

        i = base.Add(value);

     else

        Console.WriteLine("Добавление элемента запрещено." +

             "Значение = {0}", value);

     return i;

}

public override void Clear()

{

     evargs.Item=0;

     OnChanged(evargs);

     base.Clear();

}

public override object this[int index]

{

     set

     {

         evargs.Item = value;

         OnChanged(evargs);

         if (evargs.Permit)

             base[index] = value;

         else

             Console.WriteLine("Замена элемента запрещена." +

                 " Значение = {0}", value);

      }

      get{return(base[index]);}

}

Обратите внимание на схему включения события, например, в процедуре Add. Вначале задаются входные аргументы, в данном случае Item. Затем вызывается процедура включения OnChanged. При зажигании выполнение процедуры Add прерывается. Запускаются обработчики, присоединенные к событию. Процедура Add продолжит работу только после окончания их работы. Анализ выходной переменной Permit позволяет установить, получено ли разрешение на изменение значения; при истинности значения этой переменной вызывается родительский метод Add, осуществляющий изменение значения. Это достаточно типичная схема работы с событиями.

Классы receiver

Мы построим два класса, объекты которых способны получать и обрабатывать событие Changed. Получать они будут одно и то же сообщение, а обрабатывать его будут по-разному. В нашей модельной задаче различие обработчиков сведется к выдаче разных сообщений. Поэтому достаточно разобраться с устройством одного класса, названного EventReceiver1. Вот его код:

class EventReceiver1

{

     private ListWithChangedEvent List;

     public EventReceiveri(ListWithChangedEvent list)

     {

        List = list;

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

        OnCo

      }

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

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

     private void ListChanged(object sender,

          ChangedEventArgs args)

      {

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

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

      }

      public void OnCo

      {

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

           List.Changed += new ChangedEventHandler(ListChanged);

      }

       public void OffCo

      {

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

          List.Changed — = new ChangedEventHandler(ListChanged);

          List = null;

      }

}//class EventReceiver1

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

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





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

• Класс содержит метод OffCo

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

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

class Receiver2

{

     private ListWithChangedEvent List;

     public Receiver2(ListWithChangedEvent list)

     {

        List = list;

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

        OnCo

      }

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

      //Разрешает добавление элементов, меньших 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 OnCo

      {

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

          List.Changed += new ChangedEventHandler(ListChanged);

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

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

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

      }

      public void OffCo

      {

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

           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.OffCo

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

}

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

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

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

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

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

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

Слово "event" играет важную роль, позволяя решить проблему, названную нами "игнорированием коллег". В чем ее суть? В том, что некоторые из классов Receiver могут вести себя некорректно по отношению к своим коллегам, занимающимся обработкой того же события. При присоединении обработчика события в классе Receiver можно попытаться вместо присоединения обработчика выполнить операцию присваивания, игнорируя, тем самым, уже присоединенный список обработчиков. Взгляните еще раз на процедуру OnCo