Почему MSDN рекомендует включать отправителя объекта в объявлениях делегатов? - PullRequest
7 голосов
/ 25 сентября 2011

Я читал эту страницу и заметил, как она говорит, что это стандартное руководство:

Рекомендации .NET Framework указывают, что тип делегата, используемый для события, должен принимать два параметра: параметр «источник объекта», указывающий источник события, и параметр «e», который инкапсулирует любую дополнительную информацию о событии.

Я могу понять, как обладание object sender могло бы быть полезным в некоторых обстоятельствах, но я видел совершенно противоположное в других. Например,

  1. Что если класс, обрабатывающий событие, не должен знать, кто его инициировал? Сцепление, сплоченность и все такое.

  2. В моем случае у меня уже есть ссылка на объект как переменную-член. Так я подписываюсь на мероприятие. Будет только один его экземпляр, поэтому нет смысла приводить объект-отправитель, а не просто использовать переменную-член.

  3. В моей программе объект отправителя вообще не должен быть известен клиентам. Трудно объяснить, что я делаю, но в основном у меня есть класс с внутренним конструктором в библиотеке, который используется двумя другими классами также в этой библиотеке. Мои клиентские классы подписываются на события из этих двух классов, но события изначально вызываются из этого внутреннего класса, о котором клиенты не должны знать.

  4. Это сбивает с толку клиентов обработчика событий. Библиотеки должны быть просты для понимания, и в моем случае нет никаких причин использовать переменную sender. Никто. Тогда зачем его включать?

При этом, почему Microsoft указывает, что обработчики событий должны следовать этим рекомендациям? Разве это не всегда лучший выбор?

РЕДАКТИРОВАТЬ: Спасибо за ответы всем. Я решил пойти с большинством и использовать EventHandler<T> для всех моих событий в этой библиотеке.

Ответы [ 7 ]

1 голос
/ 25 сентября 2011

Почему?Люди всегда спрашивают это.В конце концов, это просто шаблон.Упаковав аргументы событий в классе, вы получите лучшую семантику управления версиями.Имея общий шаблон (sender, e), его легко узнать как сигнатуру для всех событий.Я вспоминаю, как все было плохо с Win32 - когда данные были в WPARAM по сравнению с LPARAM и так далее.Шаблон становится шумом, и разработчики просто предполагают, что обработчики событий имеют область действия для отправителя и аргументов события.

- Крис Андерсон, в Руководства по разработке структуры: условные обозначения, идиомы и шаблоны дляМногоразовые библиотеки .NET

Если вы являетесь разработчиком .NET и не читали эту книгу, вы пропускаете.Это дает вам окно;) в умы дизайнеров Microsoft .NET Framework и много лучших практик (включая их обоснование).

(Плюс, вы можете запустить FxCop , чтобы проверить, что эти методы соблюдаются.)

1 голос
/ 25 сентября 2011

Прежде всего, важно отметить, что руководство не является законом.

Весь ад (или эквивалент программиста) будет не потерять, если вы не будете следоватьруководящие принципы.

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

Однако столь же важно знать , почему эти руководящие принципы были добавлены для начала.с, и одна большая часть ответа (ов) на этот вопрос - управление версиями.

Наличие следующих двух частей и только этих двух частей:

  1. Источник события, набранный как можно более «универсальным» (обратите внимание, что сигнатуры событий были разработаны задолго до того, как в систему были введены надлежащие обобщенные элементы, поэтому object настолько универсален, насколько это было возможно тогда)
  2. Объект, унаследованный от EventArgs

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

Прежде всего, так как вам «запрещено» добавлятьили удалить параметры, все будущие версии йоВаше событие будет по-прежнему иметь только sender и e.

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

Причина этого в том, что существующий код, который уже обрабатывает ваш текущий (старый) тип, будет по-прежнему работать.

Таким образом, весь аргумент за рекомендацию состоит в том, чтобы:

  1. Оставайтесь последовательными (как уже упоминали другие)
  2. Проектируйте на будущее (убедившись, что код, написанный для версии X вашего класса, все еще работает, когда вы выпускаете версию X + 1,без ручного изменения этого кода)

Теперь, если что-то из этого не относится к вашему делу, не стесняйтесь не следовать указаниям.

На самом деле, вы можете сделатьсобытие из Action, и оно будет работать нормально.

1 голос
/ 25 сентября 2011

Вы боретесь против ветра, .NET Framework имеет определенный дизайн, правила и рекомендации, и при его использовании, если вы хотите использовать его правильно, вы должны следовать этим указаниям.

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

если вы нарушите эти правила, как я сказал недавно в своем ответе на ваш другой вопрос: Стоит ли использовать EventArgs или простой тип данных? , вы потенциально можете оказаться в ситуации, когда ваш разрывы кода.

Простое максимальное использование: когда платформа вызывает событие OnClick для элемента управления, .NET Framework действительно передает отправителя и экземпляр EventArgs ... если событие не будет соответствовать, что-то может сломаться.

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

1 голос
/ 25 сентября 2011

Я думаю, что причина в паттерне заключается в обеспечении некоторой согласованности.Параметр sender позволяет повторно использовать один обработчик для нескольких издателей (кнопок, таблиц).

Для адресации ваших очков:

1) просто не используйте его.Это обычное явление, и оно не повредит хорошей практике.

2) все в порядке, снова игнорируйте отправителя

3) полностью противоречит тому, что вы сказали в пункте 2) ...
А в остальном он такой же, как 1).Вы могли бы даже рассмотреть передачу null как отправителя.

4) «тогда зачем включать» - существуют другие варианты использования, которые требуют отправителя.


Но учтите, что это просто руководство для библиотек, подтверждающих BCL.
Ваш случай звучит больше как конкретное приложение (не библиотека), поэтому не стесняйтесь использовать любую схему параметров, которая вам нравится,Компилятор не будет жаловаться.

0 голосов
/ 09 декабря 2014

Семантически не было бы ничего плохого в том, чтобы в обработчиках событий, которые заинтересованы в происхождении событий, использовалась производная от EventArgs, включающая такое поле. Действительно, существует много ситуаций, когда это было бы чище, чем передача Sender в качестве отдельного параметра (например, если процессору сочетания клавиш нужно запустить обработчик Click для кнопки, событие на самом деле не должно рассматриваться как имеющее был поднят кнопкой, а скорее поднят от имени кнопки). К сожалению, включение этой информации в EventArgs -обработанный тип делает необходимым создание нового экземпляра EventArgs -обработанного типа каждый раз, когда возникает событие, даже если в противном случае он может использовать EventArgs.Empty. Использование отдельного параметра для передачи информации устраняет необходимость в каждом событии создавать новый экземпляр объекта в этом общем случае.

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

0 голосов
/ 25 сентября 2011

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

Я предлагаю придерживаться его, это совсем не больно.

0 голосов
/ 25 сентября 2011

Руководящие принципы, такие как это, учитывают предсказуемость со стороны потребителя события.Это также позволяет обрабатывать дополнительные сценарии, которые вы, возможно, никогда не рассматривали при создании события, особенно если ваша библиотека используется сторонними разработчиками.

Это позволяет методу, обрабатывающему событие, всегда иметь правильный экземпляр объектапод рукой, а также базовую информацию о том, почему было проведено мероприятие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...