Упаковка аргументов событий в классе, почему? - PullRequest
24 голосов
/ 25 ноября 2011

Большинство биржевых событий .NET имеют эту подпись:

delegate void SomethingSomething(SomethingEventArgs e);
event SomethingSomething OnSomethingSomething;

и

class SomethingEventArgs
{
    public string Name;
    public int Index;
    public double Groar;
}

Почему это лучше (очевидно, в противном случае любой выбрал бы это), чем:

delegate void SomethingSomething(string Name, int Index, double Groar);
event SomethingSomething OnSomethingSomething;

, так как вам не нужно упаковывать параметры в объект, и без инициализаторов (.NET 2.0) это было своего рода упражнение для набора текста.

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

Итак, почему?

Ответы [ 9 ]

30 голосов
/ 25 ноября 2011

Читать о Принцип открытия / закрытия .

Используя класс, все унаследованные классы могут вводить дополнительные функции без необходимости изменять подпись делегата. Они могут просто ввести новый класс событий (ExtendedEventArgs), который наследует ваш (SomeEventArgs).

15 голосов
/ 25 ноября 2011

Основная причина в том, что он более ремонтопригоден. Если вы передаете объект, и любое из свойств изменяется, вам нужно только изменить это. Если вы передадите переменные, это намного больше работы. Таким образом, код становится более понятным и более читаемым.

Цитата из Microsoft Код Завершен :

Ограничьте количество параметров процедуры до семи. Семерка магическое число для понимания людей. Психологическое исследование имеет обнаружили, что люди, как правило, не могут отслеживать более семи куски информации сразу (Миллер, 1956). Это открытие было применяется к огромному количеству дисциплин, и кажется безопасным для предположение, что большинство людей не могут отслеживать более семи рутинные параметры сразу.

На практике, сколько вы можете ограничить количество параметров зависит от того, как ваш язык обрабатывает сложные данные типы. Если вы программируете на современном языке, который поддерживает структурированный данные, вы можете передать составной тип данных, содержащий 13 полей и думайте об этом как об одном мысленном «кусочке» данных. Если вы программируете в более Примитивный язык, вам может понадобиться пройти все 13 полей по отдельности,

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

Цитируемый текст из оригинального изображения сообщения

11 голосов
/ 25 ноября 2011

Причина этого состоит в том, чтобы избежать критических изменений.Например, ваш класс может захотеть включить дополнительную информацию в свое событие, однако каждая вещь, которая использовала это событие, будет повреждена, так как делегат больше не будет соответствовать.Имея строгого делегата, ваше событие может инкапсулировать больше информации в будущем, не затрагивая каких-либо подписчиков.

Редактировать: согласно комментариям я расскажу, как это влияет на сокращение критических изменений.

Если мы хотим добавить дополнительную информацию к нашему возбужденному событию, используя один класс, полученный из EventArgs, могут быть добавлены новые свойства / методы.Это будет означать, что любые существующие подписчики на событие не потребуют изменений, так как добавление этих свойств не влияет на них.Единственное требуемое изменение будет в том случае, если эти свойства установлены / используются, например, когда событие вызвано.

9 голосов
/ 25 ноября 2011

Преимущество - шаблон;а наличие шаблона дает согласованность и возможность использовать другие API для нескольких типов событий:

  • Тип делегата EventHandler<T> (вам не нужно определять свойсобственный тип делегата).
  • У Reactive Extensions (Rx) есть преобразование события в IObservable<T>, позволяющее использовать LINQ для источников событий с Observable.FromEvent.

Также у вас неверные подписи:

  • Делегат принимает два аргумента: object source и SomethingEventArgs
  • Тип SomethingEventArgs наследует EventArgs.

Таким образом, ваш код должен быть примером шаблона:

В области имен:

public class SomethingEventArgs : EventArgs {
    public string Name;
    public int Index;
    public double Groar;
}

public delegate void SomethingSomething(object source, SomethingEventArgs e);

и в типе, представляющем тип

public event SomethingSomething OnSomethingSomething;

(Событие также может быть internal.)

6 голосов
/ 25 ноября 2011

Как уже отмечали другие, для этого есть причины для удобства и согласованности.Подход EventArgs также позволяет обработчику событий изменить EventArgs.

Одной из причин изменения EventArgs является обработка ошибок.Исключение, пойманное где-то в фоновом потоке, передается клиенту как событие.Клиент может установить флаг в EventArgs, чтобы указать, что исключение было обработано, и его не следует перебрасывать в фоновом потоке.

Другим примером является класс ObjectDataSource, который позволяет клиенту предоставлять экземпляр объекта, когда одиннеобходимо.Это делается путем подписки на событие ObjectDataSource.ObjectCreating и предоставления экземпляра объекта путем установки члена EventArgs.

3 голосов
/ 04 декабря 2011

Во-первых, причина , почему этот шаблон настолько распространен (как вы и просили), заключается в том, что Microsoft специально прописала , поэтому, когда они разработали шаблон событий (да, они тоже придумали этот термин). Я не обязательно думаю, что это лучше или хуже, чем придумывать собственные подписи делегатов, но следование хорошо известному соглашению может иметь свои преимущества.

По Microsoft:

  • Тип возврата Void.

  • Первый параметр называется отправителем и имеет тип Object. Это объект, вызвавший событие.

  • Второй параметр называется e и имеет тип EventArgs или производный класс EventArgs. Это специфичные для события данные.

  • Метод принимает ровно два параметра.

Однако, чтобы получить больше информации по вашему вопросу, я думаю, что обоснование использования EventArgs двоякое:

  1. Так что классы, которые наследуются от вашего класса , могут по-прежнему вызывать событие с производным классом EventArgs.
  2. Так что классы, которые обрабатывают ваше событие , могут использовать общие обработчики событий для обработки нескольких типов событий, даже если разные события используют разные EventArgs. Или, если вы измените событие в будущем, любые классы, которые уже обрабатывают событие, не должны изменять свой код, потому что обработчики все равно будут совместимы с новым событием.

Для получения дополнительной информации см. Дополнительную информацию от Microsoft:

  1. Подробнее о том, как проектировать события: http://msdn.microsoft.com/en-us/library/ms229011.aspx
  2. Подробное руководство по использованию этого шаблона: http://msdn.microsoft.com/en-us/library/w369ty8x.aspx
3 голосов
/ 01 декабря 2011

Использование класса позволяет подписчикам события влиять на результат.

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

Например:

Посмотрите на делегат FormClosingEventHandler (в Windows Forms).

Этот делегат использует параметр типа FormClosingEventArgs .

Если подписчик на событие, использующий этот делегат, устанавливает свойство Cancel (наследуется Если значение CancelEventArgs ) равно true, то форма не закрывается.

Кроме того, эти ответы также являются правильными:

  1. jgauffin
  2. Baszz
1 голос
/ 02 декабря 2011

В дополнение к соглашению о обратной совместимости, о котором уже упоминали другие, класс EventArgs обеспечивает больший контроль над непрерывным доступом к параметрам при их передаче в несколько объектов.используется для того, чтобы сделать определенные значения неизменяемыми, поэтому если событие отправляется 3 различным подписчикам, вы можете гарантировать, что оно не подделаноприходится жонглировать возвращаемыми значениями или [ref] параметрами, что значительно усложняет процесс многоадресной рассылки.Это также гарантирует, что любая логика проверки происходит в подклассе EventArgs, а НЕ в классе, который генерирует события.

1 голос
/ 25 ноября 2011

Чтобы следовать .net стандартам , рекомендуется создать событие:

  1. Создать класс, который наследуется от EventArgs
  2. Используйте EventHandler<T> универсальный делегат

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

Пример:

public event EventHandler<SometingEventArgs> SomethingEvent;

class SomethingEventArgs 
{     
      public string Name;    
      public int Index;     
      public double Groar; 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...