Почему MSDN рекомендует включать отправителя объекта в объявления делегата?

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

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

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

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

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

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

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

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

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


person Ryan Peschel    schedule 25.09.2011    source источник
comment
Я могу быть столь же последовательным в своей библиотеке, чтобы не использовать object sender   -  person Ryan Peschel    schedule 25.09.2011
comment
Это позволяет одному обработчику событий обрабатывать событие из нескольких источников событий. С отправителем, чтобы сообщить вам, откуда оно пришло. Это очевидная победа.   -  person Hans Passant    schedule 25.09.2011
comment
@HansPassant, я бы предпочел, чтобы отправитель был частью EventArgs. По крайней мере, аргументы событий должны иметь основание для существования, кроме предоставления статического пустого поля для указания отсутствия аргументов, которое в противном случае также может быть указано с помощью null.   -  person tomosius    schedule 06.10.2016


Ответы (7)


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

Чтобы ответить на ваши вопросы:

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

2) все нормально, снова игнорируем отправителя

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

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


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

person Henk Holterman    schedule 25.09.2011

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

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

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

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

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

person Davide Piras    schedule 25.09.2011
comment
+1 Это невозможно переоценить. Если вы кодируете в .NET, делайте это чистым способом. Вы не берете машину и не едете по неправильной стороне только потому, что вам не нравятся правила вождения. Любой .NET-разработчик, смотрящий на ваш код, ожидает, что он будет выглядеть определенным образом. Не ебать их. - person jgauffin; 25.09.2011

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

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

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

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

Имея следующие две части и только эти две части:

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

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

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

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

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

Итак, все доводы, лежащие в основе руководства, заключаются в следующем:

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

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

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

person Lasse V. Karlsen    schedule 25.09.2011

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

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

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

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

person TrueWill    schedule 25.09.2011

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

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

person Frazell Thomas    schedule 25.09.2011

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

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

person Michael D. Irizarry    schedule 25.09.2011

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

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

person supercat    schedule 09.12.2014