Тверской государственный университет
Опубликован: 22.11.2005 | Доступ: свободный | Студентов: 30407 / 1848 | Оценка: 4.31 / 3.69 | Длительность: 28:26:00
ISBN: 978-5-9556-0050-5
Лекция 21:

События

Классы receiver

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

class EventReceiver1
{
  private ListWithChangedEvent List;
  public EventReceiver1(ListWithChangedEvent list)
  {
    List = list;
    // Присоединяет обработчик к событию.
    OnConnect();
  }
  //Обработчик события - выдает сообщение.
  //Разрешает добавление элементов, меньших 10.
  private void ListChanged(object sender, 
    ChangedEventArgs args)
  {
    Console.WriteLine("EventReceiver1: Сообщаю об 
      изменениях:" + "Item ={0}", args.Item);
        args.Permit = ((int)args.Item < 10);
  }
  public void OnConnect()
  {
    //Присоединяет обработчик к событию
    List.Changed += new ChangedEventHandler(ListChanged);
  }
  public void OffConnect()
  {
    //Отсоединяет обработчик от события и удаляет список
    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(list1);
  Random rnd = new Random();
  //Работа с объектами, приводящая к появлению событий
  list.Add(rnd.Next(20)); list.Add(rnd.Next(20)); 
    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 ловятся еще на этапе трансляции.

Александр Галабудник
Александр Галабудник

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

Александра Гусева
Александра Гусева