.NET EventHandlers - общий или нет?

Каждый раз, когда я начинаю работать над проектом С#, я получаю множество событий, которые просто нужно передать одному элементу. Я придерживаюсь практики EventHandler/EventArgs, но мне нравится делать что-то вроде:

public delegate void EventHandler<T>(object src, EventArgs<T> args);

public class EventArgs<T>: EventArgs {

  private T item;

  public EventArgs(T item) {
    this.item = item;
  }

  public T Item {
    get { return item; }
  }
}

Позже у меня может быть

public event EventHandler<Foo> FooChanged;

public event EventHandler<Bar> BarChanged;

Однако, кажется, что стандартом для .NET является создание нового делегата и подкласса EventArgs для каждого типа события. Что-то не так с моим общим подходом?


EDIT: Причина этого сообщения в том, что я только что заново создал это в новом проекте и хотел убедиться, что все в порядке. На самом деле, я воссоздал его, когда я разместил его. Я обнаружил, что существует общий EventHandler<TEventArgs>, поэтому вам не нужно создавать общий делегат, но вам все еще нужен общий EventArgs<T> класс, потому что TEventArgs: EventArgs.
Другой EDIT: Один недостаток (для меня) встроенного решения - дополнительная многословность:
public event EventHandler<EventArgs<Foo>> FooChanged;

против.

public event EventHandler<Foo> FooChanged;

Для клиентов может быть больно регистрироваться для ваших событий, потому что пространство имен System по умолчанию импортировано по умолчанию, поэтому им приходится вручную искать пространство имен даже при помощи привлекательного инструмента, такого как Resharper... У любого есть идеи относящиеся к этому?

Ответ 1

Добавлен делегат из следующей формы, поскольку .NET Framework 2.0

public delegate void EventHandler<TArgs>(object sender, TArgs args) where TArgs : EventArgs

Вы подходите немного дальше, поскольку вы предоставляете готовые решения для EventArgs с единым элементом данных, но у него нет нескольких свойств исходной идеи:

  • Вы не можете добавлять дополнительные данные к данным события без изменения зависимого кода. Вам нужно будет изменить подпись делегата, чтобы предоставить больше данных подписчику события.
  • Ваш объект данных является общим, но он также "анонимный", и при чтении кода вам придется расшифровать свойство "Item" из обычаев. Он должен быть назван в соответствии с данными, которые он предоставляет.
  • Используя generics таким образом, вы не можете выполнить параллельную иерархию EventArgs, когда у вас есть иерархия базовых типов (элементов). Например. EventArgs <BaseType> не является базовым типом для EventArgs <DerivedType> , даже если BaseType является базой для DerivedType.

Итак, я думаю, что лучше использовать общий EventHandler <T> , но все еще имеют пользовательские классы EventArgs, организованные в соответствии с требованиями модели данных. С Visual Studio и расширениями, такими как ReSharper, для создания нового класса требуется всего несколько команд.

Ответ 2

Чтобы упростить общее объявление события, я создал для него пару фрагментов кода. Чтобы использовать их:

  • Скопируйте весь фрагмент.
  • Вставьте его в текстовый файл (например, в Блокнот).
  • Сохраните файл с расширением .snippet.
  • Поместите файл .snippet в соответствующий каталог фрагментов, например:

Visual Studio 2008\Фрагменты кода \Visual С#\Фрагменты моего кода

Вот тот, который использует пользовательский класс EventArgs с одним свойством:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Generic event with one type/argument.</Title>
      <Shortcut>ev1Generic</Shortcut>
      <Description>Code snippet for event handler and On method</Description>
      <Author>Ryan Lundy</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>type</ID>
          <ToolTip>Type of the property in the EventArgs subclass.</ToolTip>
          <Default>propertyType</Default>
        </Literal>
        <Literal>
          <ID>argName</ID>
          <ToolTip>Name of the argument in the EventArgs subclass constructor.</ToolTip>
          <Default>propertyName</Default>
        </Literal>
        <Literal>
          <ID>propertyName</ID>
          <ToolTip>Name of the property in the EventArgs subclass.</ToolTip>
          <Default>PropertyName</Default>
        </Literal>
        <Literal>
          <ID>eventName</ID>
          <ToolTip>Name of the event</ToolTip>
          <Default>NameOfEvent</Default>
        </Literal>
      </Declarations>
      <Code Language="CSharp"><![CDATA[public class $eventName$EventArgs : System.EventArgs
      {
        public $eventName$EventArgs($type$ $argName$)
        {
          this.$propertyName$ = $argName$;
        }

        public $type$ $propertyName$ { get; private set; }
      }

      public event EventHandler<$eventName$EventArgs> $eventName$;
            protected virtual void On$eventName$($eventName$EventArgs e)
            {
                var handler = $eventName$;
                if (handler != null)
                    handler(this, e);
            }]]>
      </Code>
      <Imports>
        <Import>
          <Namespace>System</Namespace>
        </Import>
      </Imports>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

А вот тот, который имеет два свойства:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Generic event with two types/arguments.</Title>
      <Shortcut>ev2Generic</Shortcut>
      <Description>Code snippet for event handler and On method</Description>
      <Author>Ryan Lundy</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>type1</ID>
          <ToolTip>Type of the first property in the EventArgs subclass.</ToolTip>
          <Default>propertyType1</Default>
        </Literal>
        <Literal>
          <ID>arg1Name</ID>
          <ToolTip>Name of the first argument in the EventArgs subclass constructor.</ToolTip>
          <Default>property1Name</Default>
        </Literal>
        <Literal>
          <ID>property1Name</ID>
          <ToolTip>Name of the first property in the EventArgs subclass.</ToolTip>
          <Default>Property1Name</Default>
        </Literal>
        <Literal>
          <ID>type2</ID>
          <ToolTip>Type of the second property in the EventArgs subclass.</ToolTip>
          <Default>propertyType1</Default>
        </Literal>
        <Literal>
          <ID>arg2Name</ID>
          <ToolTip>Name of the second argument in the EventArgs subclass constructor.</ToolTip>
          <Default>property1Name</Default>
        </Literal>
        <Literal>
          <ID>property2Name</ID>
          <ToolTip>Name of the second property in the EventArgs subclass.</ToolTip>
          <Default>Property2Name</Default>
        </Literal>
        <Literal>
          <ID>eventName</ID>
          <ToolTip>Name of the event</ToolTip>
          <Default>NameOfEvent</Default>
        </Literal>
      </Declarations>
      <Code Language="CSharp">
        <![CDATA[public class $eventName$EventArgs : System.EventArgs
      {
        public $eventName$EventArgs($type1$ $arg1Name$, $type2$ $arg2Name$)
        {
          this.$property1Name$ = $arg1Name$;
          this.$property2Name$ = $arg2Name$;
        }

        public $type1$ $property1Name$ { get; private set; }
        public $type2$ $property2Name$ { get; private set; }
      }

      public event EventHandler<$eventName$EventArgs> $eventName$;
            protected virtual void On$eventName$($eventName$EventArgs e)
            {
                var handler = $eventName$;
                if (handler != null)
                    handler(this, e);
            }]]>
      </Code>
      <Imports>
        <Import>
          <Namespace>System</Namespace>
        </Import>
      </Imports>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

Вы можете следовать шаблону, создавая их с любым количеством свойств.

Ответ 3

Нет, я не думаю, что это неправильный подход. Я думаю, что это даже рекомендуется в [фантастической] книге Framework Design Guidelines. Я делаю то же самое.

Ответ 4

Это правильная реализация. Он был добавлен в .NET Framework (mscorlib), так как в начале появились generics (2.0).

Подробнее об использовании и реализации см. в MSDN: http://msdn.microsoft.com/en-us/library/db0etb8x.aspx

Ответ 5

В первый раз, когда я увидел этот небольшой шаблон, я использовал Composite UI Application block из группы шаблонов и практик MS.

Он не бросает мне никакого красного флага; на самом деле это даже разумный способ использования дженериков для выполнения правила DRY.

Ответ 6

Так как .NET 2.0

EventHandler<T>

.

Ответ 7

Вы можете найти Generic EventHandler на MSDN http://msdn.microsoft.com/en-us/library/db0etb8x.aspx

Я широко использовал общий EventHandler и смог предотвратить так называемый "Взрыв типов (классов)" Проект был меньше и проще ориентироваться.

Принятие нового интуитивного делегата для неэквивалентного делегата EventHandler болезненно и накладывается на существующие типы Добавление "* EventHandler" к новому имени делегата на мой взгляд не очень помогает

Ответ 8

Я действительно верю, что в последних версиях .NET есть только такой обработчик событий, определенный в них. Это большие пальцы, насколько мне известно.

/EDIT

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

Ответ 9

Использовать общие экземпляры обработчика событий

Перед .NET Framework 2.0 для передачи пользовательской информации обработчику события должен был быть объявлен новый делегат, который задал класс, полученный из класса System.EventArgs. Это больше не относится к .NET

Framework 2.0, в котором был представлен делегат System.EventHandler <T> ). Этот общий делегат позволяет использовать любой класс, полученный из EventArgs, с обработчиком событий.