Подтвердить что ты не робот

Событие Действие <> против события EventHandler <>

Есть ли разница между объявлением event Action<> и event EventHandler<>.

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

например:

public event Action<bool, int, Blah> DiagnosticsEvent;

против

public event EventHandler<DiagnosticsArgs> DiagnosticsEvent;

class DiagnosticsArgs : EventArgs
{
    public DiagnosticsArgs(bool b, int i, Blah bl)
    {...}
    ...
}
В обоих случаях использование

будет почти одинаковым:

obj.DiagnosticsEvent += HandleDiagnosticsEvent;

Есть несколько вещей, которые мне не нравятся в шаблоне event EventHandler<>:

  • Объявление дополнительного типа, полученное из EventArgs
  • Принудительное прохождение источника объекта - часто никто не заботится

Дополнительный код означает больше кода для поддержки без каких-либо явных преимуществ.

В результате я предпочитаю event Action<>

Однако, только если в Action < > слишком много аргументов типа, тогда потребуется дополнительный класс.

4b9b3361

Ответ 1

Основное отличие будет заключаться в том, что если вы используете Action<>, ваше событие не будет следовать шаблону проектирования практически любого другого события в системе, что я считаю недостатком.

Один верх с доминирующим шаблоном проектирования (помимо силы однообразия) заключается в том, что вы можете расширить объект EventArgs новыми свойствами без изменения сигнатуры события. Это было бы возможно, если вы использовали Action<SomeClassWithProperties>, но я действительно не вижу смысла не использовать регулярный подход в этом случае.

Ответ 2

Основываясь на некоторых предыдущих ответах, я собираюсь разбить свой ответ на три области.

Во-первых, физические ограничения использования Action<T1, T2, T2... > vs с использованием производного класса EventArgs. Есть три: во-первых, если вы измените число или типы параметров, каждый метод, который подписывается, должен быть изменен, чтобы соответствовать новому шаблону. Если это публичное событие, которое будут использовать сторонние сборки, и есть вероятность, что переменные событий будут меняться, это может послужить основанием для использования пользовательского класса, полученного из аргументов событий для согласованности (помните, что вы ДОЛЖНЫ используйте Action<MyCustomClass>). Во-вторых, использование Action<T1, T2, T2... > не позволит вам передать обратную связь BACK к вызывающему методу, если у вас нет какого-либо объекта (например, с использованием свойства Handled), который передается вместе с действием. В-третьих, вы не получаете именованных параметров, поэтому, если вы передаете 3 bool a int, два string и a DateTime, вы не знаете, что означает эти значения. В качестве побочного примечания вы все равно можете безопасно использовать "Огонь этого события" при использовании Action<T1, T2, T2... > ".

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

В-третьих, реальная практика жизни, я лично считаю, что я, как правило, создаю много событий для вещей, таких как изменения свойств, с которыми мне нужно взаимодействовать (особенно при работе с MVVM с моделями просмотра, которые взаимодействуют друг с другом) или где событие имеет один параметр. Большую часть времени эти события принимают форму public event Action<[classtype], bool> [PropertyName]Changed; или public event Action SomethingHappened;. В этих случаях есть два преимущества. Во-первых, я получаю тип для класса выдачи. Если MyClass объявляет и является единственным классом, запускающим событие, я получаю явный экземпляр MyClass для работы с обработчиком события. Во-вторых, для простых событий, таких как события изменения свойств, значение параметров очевидно и указано в имени обработчика событий, и мне не нужно создавать множество классов для этих событий.

Ответ 3

По большей части, я бы сказал, следуйте шаблону. Я отклонился от него, но очень редко, и по определенным причинам. В этом случае самая большая проблема, с которой я столкнулся, это то, что я, вероятно, все еще буду использовать Action<SomeObjectType>, что позволит мне добавить дополнительные свойства позже и использовать случайное двухстороннее свойство (подумайте Handled, или другие события обратной связи, в которых абоненту необходимо установить свойство объекта события). И как только вы запустили эту строку, вы можете использовать EventHandler<T> для некоторых T.

Ответ 4

Преимущество подхода Worder заключается в том, что ваш код находится внутри проекта в 300 000 строк.

Используя действие, как и у вас, нет способа рассказать мне, что такое bool, int и Blah. Если ваше действие передало объект, который определил параметры, тогда ok.

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

Ответ 5

Если вы следуете стандартным шаблонам событий, вы можете добавить метод расширения, чтобы сделать проверку срабатывания событий более безопасной/легкой. (т.е. следующий код добавляет метод расширения, называемый SafeFire(), который выполняет нулевую проверку, а также (очевидно) копирование события в отдельную переменную, чтобы быть в безопасности от обычного состояния гонки, которое может повлиять на события.)

(Хотя я имею в виду два ума, следует ли использовать методы расширения для нулевых объектов...)

public static class EventFirer
{
    public static void SafeFire<TEventArgs>(this EventHandler<TEventArgs> theEvent, object obj, TEventArgs theEventArgs)
        where TEventArgs : EventArgs
    {
        if (theEvent != null)
            theEvent(obj, theEventArgs);
    }
}

class MyEventArgs : EventArgs
{
    // Blah, blah, blah...
}

class UseSafeEventFirer
{
    event EventHandler<MyEventArgs> MyEvent;

    void DemoSafeFire()
    {
        MyEvent.SafeFire(this, new MyEventArgs());
    }

    static void Main(string[] args)
    {
        var x = new UseSafeEventFirer();

        Console.WriteLine("Null:");
        x.DemoSafeFire();

        Console.WriteLine();

        x.MyEvent += delegate { Console.WriteLine("Hello, World!"); };
        Console.WriteLine("Not null:");
        x.DemoSafeFire();
    }
}

Ответ 6

Глядя на Стандартные шаблоны событий .NET, мы находим

Стандартная подпись для делегата .NET event:

void OnEventRaised(object sender, EventArgs args);

[...]

Список аргументов содержит два аргумента: отправитель и аргументы события. Тип времени компиляции отправителя - System.Object, хотя вы, вероятно, знаете более производный тип, который всегда был бы правильным. По соглашению используйте объект.

Ниже на той же странице мы находим пример типичного определения события, что-то вроде

public event EventHandler<EventArgs> EventName;

Если бы мы определили

class MyClass
{
  public event Action<MyClass, EventArgs> EventName;
}

обработчик мог бы быть

void OnEventRaised(MyClass sender, EventArgs args);

где sender имеет правильный (более производный) тип.