мероприятия акции против 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<>
однако, только если в действии слишком много аргументов типа, то потребуется дополнительный класс.
6 ответов:
основная разница будет в том, что если вы используете
Action<>ваше событие не будет следовать шаблону проектирования практически любого другого события в системе, что я бы счел недостатком.один плюс с доминирующим рисунком дизайна (помимо силы одинаковости) заключается в том, что вы можете расширить
EventArgsобъект с новыми свойствами без изменения сигнатуры события. Это все еще было бы возможно, если бы вы использовалиAction<SomeClassWithProperties>, но я не вижу смысла не использовать регулярный подход в этом случае.
исходя из предыдущих ответов, я разобью свой ответ на три зоны.
во-первых, физические ограничения использования
Action<T1, T2, T2... >vs с использованием производного классаEventArgs. Во-первых, если вы измените количество или типы параметров, каждый метод, который подписывается, должен быть изменен, чтобы соответствовать новому шаблону. Если это публичное событие, которое будут использовать сторонние сборки, и есть какая-либо возможность, что args события изменение, это было бы причиной использовать пользовательский класс, производный от событий args для консистенции (помните, что вы все еще можете использоватьAction<MyCustomClass>) во-вторых, используяAction<T1, T2, T2... >будет препятствовать передаче обратной связи обратно вызывающему методу, если у вас нет какого-либо объекта (например, с обрабатываемым свойством), который передается вместе с действием. В-третьих, вы не получаете именованные параметры, так что если вы передаете 3bool'ыint, дваstringиDateTime, вы понятия не имеете, что смысл этих ценностей. в качестве примечания, вы все еще можете иметь " огонь это событие безопасно метод, все еще используяAction<T1, T2, T2... >".во-вторых, последствия консистенции. Если у вас есть большая система, с которой вы уже работаете, почти всегда лучше следовать тому, как спроектирована остальная система, если у вас нет очень веской причины. Если вы публично столкнулись с событиями, которые необходимо поддерживать, возможность замены производных классов может быть важный. Имейте это в виду.
в-третьих, реальная жизненная практика, я лично считаю, что я склонен создавать много событий для таких вещей, как изменения свойств, с которыми мне нужно взаимодействовать (особенно при выполнении MVVM с моделями представления, которые взаимодействуют друг с другом) или где событие имеет один параметр. Большую часть времени эти события принимают форму
public event Action<[classtype], bool> [PropertyName]Changed;илиpublic event Action SomethingHappened;. В этих случаях есть два преимущества. Во-первых, я получаю тип для выдающего класса. ЕслиMyClassобъявляет и является единственным классом, запускающим событие, я получаю явный экземплярMyClassдля работы с обработчиком событий. Во-вторых, для простых событий, таких как события изменения свойств, значение параметров очевидно и указано в имени обработчика событий, и мне не нужно создавать множество классов для этих видов событий.
по большей части, я бы сказал, следовать шаблону. Я есть отклонился от него, но очень редко, и по определенным причинам. В данном случае самая большая проблема, с которой я столкнулся бы, заключается в том, что я, вероятно, все еще использую
Action<SomeObjectType>, что позволяет мне добавлять дополнительные свойства позже и использовать случайное свойство 2-way (thinkHandled, или другие события обратной связи, где абонент должен 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