мероприятия акции против EventHandler в случае



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



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



например:



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


vs



public event EventHandler<DiagnosticsArgs> DiagnosticsEvent;

class DiagnosticsArgs : EventArgs
{
public DiagnosticsArgs(bool b, int i, Blah bl)
{...}
...
}


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



obj.DiagnosticsEvent += HandleDiagnosticsEvent;


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




  • объявление дополнительного типа, полученное из
    EventArgs

  • обязательное прохождение исходный объект –
    часто никто не заботится


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



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



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

311   6  
c#

6 ответов:

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

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

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

во-первых, физические ограничения использования Action<T1, T2, T2... > vs с использованием производного класса EventArgs. Во-первых, если вы измените количество или типы параметров, каждый метод, который подписывается, должен быть изменен, чтобы соответствовать новому шаблону. Если это публичное событие, которое будут использовать сторонние сборки, и есть какая-либо возможность, что args события изменение, это было бы причиной использовать пользовательский класс, производный от событий args для консистенции (помните, что вы все еще можете использовать Action<MyCustomClass>) во-вторых, используя Action<T1, T2, T2... > будет препятствовать передаче обратной связи обратно вызывающему методу, если у вас нет какого-либо объекта (например, с обрабатываемым свойством), который передается вместе с действием. В-третьих, вы не получаете именованные параметры, так что если вы передаете 3 boolint, два stringи DateTime, вы понятия не имеете, что смысл этих ценностей. в качестве примечания, вы все еще можете иметь " огонь это событие безопасно метод, все еще используя Action<T1, T2, T2... >".

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

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

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

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

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

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

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

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

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();
    }
}

смотрим стандартные шаблоны событий .NET найти у нас

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

void OnEventRaised(object sender, EventArgs args);

[...]

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

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

public event EventHandler<EventArgs> EventName;

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

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

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

void OnEventRaised(MyClass sender, EventArgs args);

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

Comments

    Ничего не найдено.