Существует стандартный шаблон для событий в .NET - они используют тип delegate
, который принимает простой объект с именем sender, а затем фактическую «полезную нагрузку» во втором параметре, который должен быть получен из EventArgs
.
Логическое обоснование второго параметра, полученного из EventArgs
, кажется довольно ясным (см. Справочник по стандартной библиотеке .NET Framework ). Он предназначен для обеспечения двоичной совместимости между приемниками событий и источниками по мере развития программного обеспечения. Для каждого события, даже если оно имеет только один аргумент, мы извлекаем собственный класс аргументов события, который имеет одно свойство, содержащее этот аргумент, так что мы сохраняем возможность добавлять дополнительные свойства к полезной нагрузке в будущих версиях, не нарушая существующий клиентский код , Очень важно в экосистеме самостоятельно разработанных компонентов.
Но я считаю, что то же самое относится и к нулевым аргументам. Это означает, что если у меня есть событие, которое не имеет аргументов в моей первой версии, и я пишу:
public event EventHandler Click;
... тогда я делаю это неправильно. Если в будущем я изменю тип делегата на новый класс в качестве его полезной нагрузки:
public class ClickEventArgs : EventArgs { ...
... Я нарушу бинарную совместимость с моими клиентами. Клиент в конечном итоге привязан к определенной перегрузке внутреннего метода add_Click
, который принимает EventHandler
, и если я изменю тип делегата, он не сможет найти эту перегрузку, поэтому есть MissingMethodException
.
Хорошо, а что если я использую удобную универсальную версию?
public EventHandler<EventArgs> Click;
Нет, все еще не так, потому что EventHandler<ClickEventArgs>
- это не EventHandler<EventArgs>
.
Таким образом, чтобы получить выгоду от EventArgs
, у вас есть , чтобы извлекать из него пользу, а не использовать его непосредственно как есть. Если вы этого не сделаете, вы можете не использовать его (мне кажется).
Тогда есть первый аргумент, sender
. Мне кажется это как рецепт нечестивой связи. Событие запуска по сути является вызовом функции. Должна ли функция, вообще говоря, иметь возможность копаться в стеке и выяснять, кто был вызывающим абонентом, и соответствующим образом корректировать свое поведение? Должны ли мы сделать так, чтобы интерфейсы выглядели так?
public interface IFoo
{
void Bar(object caller, int actualArg1, ...);
}
В конце концов, разработчик Bar
может захотеть узнать, кем был caller
, чтобы он мог запросить дополнительную информацию! Я надеюсь, что ты сейчас вырвался. Почему это должно быть иначе для событий?
Поэтому, даже если я готов взять на себя создание отдельного класса EventArgs
для каждого события, которое я объявляю, просто для того, чтобы он стоил моего при использовании EventArgs
, я определенно предпочел бы отказаться от аргумент отправителя объекта.
Функция автозаполнения Visual Studio, похоже, не заботится о том, какой делегат вы используете для события - вы можете набрать +=
[Hit Space, Return] , и она напишет для вас метод-обработчик, который соответствует любому делегировать это случается.
Так какое значение я бы потерял, отклонившись от стандартного шаблона?
В качестве дополнительного вопроса, будет ли C # / CLR 4.0 делать что-нибудь, чтобы изменить это, возможно, с помощью контравариантности в делегатах? Я попытался исследовать это, но нажал другую проблему . Первоначально я включил этот аспект вопроса в этот другой вопрос, но это вызвало путаницу. И, кажется, немного разбить это на три вопроса ...
Обновление:
Оказывается, я был прав, задаваясь вопросом о влиянии контравариантности на весь этот вопрос!
Как отмечалось в 1063 *, новые правила компилятора оставляют дыру в системе типов, которая взрывается во время выполнения. Отверстие было эффективно закрыто, определяя EventHandler<T>
иначе, чем Action<T>
.
Так что для событий, чтобы избежать этой дыры, вы не должны использовать Action<T>
. Это не значит, что вы должны использовать EventHandler<TEventArgs>
; это просто означает, что если вы используете универсальный тип делегата, не выбирайте тот, который включен для контравариантности.