.Net: События в С#

Введение
Концептуальная роль событий заключается в следующем: Если какой-то объект хочет оповестить других о смене своего состояния, он запускает событие (или сигнал). Это событие может быть отловлено любым количеством объектом. Реакцией на событие, как правило, является вызов метода в отлавливающем объекте.
В языке C# события являются более развитой системой использование групповых делегатов.
При создании события его необходимо объявить:
   
< Модификатор доступа > event < Тип делегата > < Имя события>;

…таким образом событие имеет сигнатуру делегата. Вызов события происходит также как и вызов делегата.

Работа с событиями
Работа с событиями состоит из 3-х важных пунктов:
1. Создание события.
2. Регистрация обработчика события.
3. Генерация события.

public delegate void Process(int val);
  public class ExampleClass
  {
    private int _field;
    public event Process NegativeValue; //объявление события
    public void SetField(int newValue)
    {
      _field = newValue;
      if (newValue < 0)
      {
        if (NegativeValue != null)        
          NegativeValue(newValue);        
      }
    }
  }

Рассмотрим этап регистрации получателя события. Для того чтобы отреагировать на событие, его надо ассоциировать с обработчиком события.
Обработчиком события может быть метод, совпадающий по типу с типом события (делегатом). В качестве обработчика может выступать анонимный
метод или лямбда-оператор.
class Program
    {
      public static void Reaction(int i)
      { 
        Console.WriteLine("{0} is a negative value", i);
      }
      public static void Main()
      {
        var c = new ExampleClass();
        c.SetField(-200); // Нет обработчиков, нет и реакции.
        //Если бы при генерации события отсутствовала проверка на null, то предыдущая строка вызвала бы исключительную ситуацию       
        c.NegativeValue += Reaction; // Назначаем обработчик. Теперь будет вывод "-10 is a negative value"
        c.SetField(-10);   // Назначаем еще один обработчик
        c.NegativeValue += i => Console.WriteLine(i); // Используем лямбда-оператор.      
        c.SetField(-10); //Вывод: "-10 is a negative value" и "-10" 
        c.NegativeValue -= Reaction;  // Удаляем первый обработчик
      }
    }

В реальности, событие это набор методов: первый начинается с приставки add_, а второй — с приставки remove_, что становится прекрасно видно с помощью дизассемблера IL.

    image

Определение события в одной строке является фактически сокращенной версией записи методов, работающих с обработчиками.
Методы добавления и удаления делегата из события генерируются автоматически, однако программист может их переопределить, используя следующую запись:
event <имя делегата> <имя события>
{
   add { }
   remove { }
};


Что в принципе удобно, если необходимо осуществить более сложные операции, чем простое добавление и удаление обработчика.
Т.к. события создаются на основе делегатов, которые могут указывать не только на один метод, то соотвтественно и события могут «возбуждать» сразу несколько обработчиков. Многоадресные события позволяют множеству объектов «реагировать» на извещение о событии.

Стандартные делегаты
.NET предлагает средства для упрощения работы с событиями. Имеются следующие стандартные делегаты:
public delegate void EventHandler(object sender, EventArgs e);
public delegate void EventHandler(object sender,TEventArgs e)
   where TEventArgs : EventArgs;
   object sender – объект, который генерирует событие.
   EventArgs e – объект, содержащий аргументы( параметры ) события

Внесём изменения в код класса ExampleClass для работы с вышеперечисленными делегатами.

  public class MyEventArg : EventArgs
   {
     public int NewValue { get; private set; }
     public MyEventArg(int newValue)
      {
        NewValue = newValue;
      }
   }
  public class ExampleClass
   {
    private int _field;
    public event EventHandler NegativeValue;
    public void SetField(int newValue)
    {
      _field = newValue;
       if (newValue < 0)      
        OnNegativeValueSet(newValue);      
    }
    protected virtual void OnNegativeValueSet(int value)
    {
     if (NegativeValue != null)
       NegativeValue(this, new MyEventArg(value));     
    }
   }
Методы расширения и события

Класс EventHelper содержит несколько методов расширения, упрощающих генерацию событий.
  public event EventHandler<EventArgs> FrameSent;
  public class EventArgs : EventArgs
  {
    public T EventInfo { get; private set; }
    public EventArgs(T eventInfo)
    {
      EventInfo = eventInfo;
    }
  }
  public static class EventHelper
  {
    public static void Raise(this EventHandler<EventArgs> eventHandler,
      object sender, T args)
    {
      if (eventHandler != null)
         eventHandler(sender, new EventArgs(args));    
    }

    public static void Raise(this EventHandler<EventArgs> eventHandler, T args)
    {
        if (eventHandler != null)
          eventHandler(null, new EventArgs(args));    
    }
    public static void Raise(this EventHandler eventHandler,object sender, EventArgs args)
    {
      if (eventHandler != null)
         eventHandler(sender, args);   
    }
  

Пример использования метода расширения из класса EventHelper:
    FrameSent.Raise(this, frame);
Это позволяет намного упростить весь процесс работы с событиями.


Пример использования метода расширения из класса EventHelper:
    
FrameSent.Raise(this, frame);

Это позволяет намного упростить весь процесс работы с событиями.


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.