Собрание событий - я не понимаю - PullRequest
6 голосов
/ 19 апреля 2010

Мой класс с событием:

public class WindowModel
{
    public delegate void WindowChangedHandler(object source, WindowTypeEventArgs e);
    public event WindowChangedHandler WindowChanged;  

    public void GotoWindow(WindowType windowType)
    {
        this.currentWindow = windowType;
        this.WindowChanged.Invoke(this, new WindowTypeEventArgs(windowType));
    }
}

Производный класс событий:

public class WindowTypeEventArgs : EventArgs
{
    public readonly WindowType windowType;

    public WindowTypeEventArgs(WindowType windowType)
    {
        this.windowType = windowType;
    }
}

Какой-то другой класс, который регистрирует его на событие:

private void SetupEvents()
{
   this.WindowModel.WindowChanged += this.ChangeWindow;
}

private void ChangeWindow(object sender, WindowTypeEventArgs e)
{
   //change window
}

Что я получил от соблюдения соглашения .Net? Было бы более разумно иметь такой контракт

public delegate void WindowChangedHandler(WindowType windowType);
public event WindowChangedHandler WindowChanged;

Делая это таким образом, мне не нужно создавать новый класс, и его легче понять. Я не кодирую библиотеку .Net. Этот код будет использоваться только в этом проекте. Мне нравятся соглашения, но я прав, когда говорю, что в этом примере это не имеет смысла, или я что-то не так понял?

Ответы [ 3 ]

15 голосов
/ 19 апреля 2010

В отдельности да, вы правы: обычный синтаксис .NET более многословен и менее интуитивен, но имеет свои преимущества:

  • Будущие изменения в информации, передаваемой вашим событием, не требуют автоматических изменений для каждого потребителя события. Например, если вы хотите добавить дополнительную информацию к вашему событию - скажем, строку WindowTitle - вам придется изменить сигнатуру каждой функции, которая присоединяется к этому событию, независимо от того, не они используют это. При использовании подхода EventArgs вы добавляете свойство к аргументам и изменяете только те функции, которые необходимы для получения дополнительной информации.
  • Поскольку в .NET 2.0 появился тип делегата EventHandler<TEventArgs>, вам больше не нужно определять свои собственные делегаты событий вручную. В вашем примере вы бы набрали ваше событие как EventHandler<WindowTypeEventArgs> вместо WindowChangedHandler.
  • Подход EventArgs упрощает передачу нескольких типов информации обратно вызывающей функции. Если вам нужно сделать это в альтернативном примере (который передает параметр события напрямую), вам все равно придется создать собственный класс -tuple для хранения информации.

Влияние первого становится более очевидным, когда вы смотрите на шаблон фактический для событий .NET при создании функции protected virtual, которая фактически выполняет вызов. Например:

public event EventHandler<WindowTypeEventArgs> WindowChanged;

protected virtual void OnWindowChanged(WindowTypeEventArgs e)
{
    var evt = WindowChanged;

    if(evt != null) evt(this, e);
}

Я бы хотел отметить пару вещей:

  1. Использование шаблона создания этого метода, вызывающего события, позволяет избежать проверки на нулевые значения во всем коде (событие без каких-либо прикрепленных к нему функций будет null и вызовет исключение, если вы попытаетесь вызвать его)
  2. Этот шаблон также позволяет классам, которые наследуются от вас, контролировать порядок вызовов, что позволяет им явно выполнять свой код либо до, либо после любых внешних потребителей
  3. Это особенно важно в многопоточных средах. Если бы вы просто сказали if(WindowChanged != null) WindowChanged(this, e);, вы на самом деле рискуете, если событие WindowChanged станет null между моментом его проверки и временем его вызова. Это не важно делать в однопоточных сценариях, но это хорошая защитная привычка.
3 голосов
/ 19 апреля 2010

Я узнаю ваше замешательство! У меня было то же чувство, когда я впервые посмотрел на это.

Важно понимать, что это не дает вам большого преимущества в программном плане, но это соглашение, хорошо известное в фреймворке. Таким образом, существует множество инструментов, которые ожидают подпись void EventName(object sender, EventArgs e). Например, некоторые контейнеры IoC могут использовать эту подпись для автоматической передачи событий во время строительства.

Короче, это выглядит немного странно, но это соглашение. Придерживайтесь этого, и лампочка загорится в конце концов!

1 голос
/ 19 апреля 2010

Вы можете использовать свой делегат. Никто не заставит тебя. Это просто хороший пример для событий.

Если вы используете стандартный шаблон Sender-EventArgs, вы сможете использовать тот же обработчик ChangeWindow для других событий.

...