Должен ли отправитель события всегда быть универсальным объектом?

10

При программировании событий на C # рекомендуется создать делегат в форме:

delegate XEventHandler(object sender, XEventArgs e);

У меня вопрос по первому аргументу делегата object sender. Это всегда должно быть универсальным object? Наличие отправителя типа objectвсегда приводит к коду, подобному этому.

val = ((ConcreteType)sender).Property;

или, еще более многословный,

ConcreteType obj = sender as ConcreteType
if (obj != null) { ... }

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

Что если класс отправителя всегда известен (по крайней мере, как абстрактный класс)? Например, если я реализую ListChangedсобытие в абстрактном Listклассе и если другие классы собираются его унаследовать (например LinkedList, ArrayList), можно ли определить мой делегат с отправителем типа List?

delegate ListChangedEventHander(List sender, ListChangedEventArgs e);

Или есть ли обратная сторона в изменении обычного типа object senderна более конкретный?

sampathsris
источник

Ответы:

10

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

В Руководстве по разработке мероприятий говорится:

DO использовать objectкак тип первого параметра обработчика событий, и назовите его sender.

Тем не менее, вы можете заметить, что в текущем руководстве говорится, что вы не должны определять свой собственный делегат для событий, а использовать его EventHandler<T>, если можете.

Что касается дизайна, я бы предположил, что он также способствует повторному использованию обработчиков событий, даже в тех контекстах, которые изначально не были предусмотрены первоначальным разработчиком события.

jhominal
источник
2
Тьфу ... просто потому, что Microsoft решила сделать их руководящие принципы достаточно общими, чтобы их можно было применять ко всем, это не значит, что всем остальным будет полезно следовать их указаниям. Весьма вероятно, что «все остальные» не пишут код, который будет использоваться миллионами других разработчиков. Я собрал около 2/3 рекомендаций по предоставленной вами ссылке.
Данк
По правде говоря, это «руководство» более или менее похоже на венгерскую нотацию Windows API. Кто-то блестящий начал это по действительно веской причине, а затем все остальные начали злоупотреблять этим. Когда есть руководство, лучше иметь вескую причину этого руководства. И я думаю, что причина этого правила в том, что System.Windows.Formsпространство имен - это место, где события используются наиболее интенсивно, и имело смысл подписаться на Clickсобытие Buttonили CheckBox. Таким образом, отправитель должен быть общим. ...
Сампатрисрис
... Но когда дело доходит до других, более конкретных, ограниченных областей функциональности, отправителю может не понадобиться универсальный класс. Смотрите снова мой пример иерархии классов Lists.
Сампатрисрис
11
@ Dunk: И в этом все дело. Это руководство взято из Руководства по проектированию платформы, которое в первую очередь касается кода, используемого другими . Не потому что это лучшее решение, а потому что это наименее удивительно. Это лучший вариант для фреймворка . Для небольших библиотек, если прецедент четко указан, применимо меньше руководств. Microsoft прямо говорит об этом в начале книги.
Волхв
1
Я не спорю с ответом, я даже проголосовал за него, потому что это из авторитетного источника. Я просто говорю, что я не буду использовать руководящие принципы. Если событие должно отправить конкретные данные, я просто отправлю конкретные данные. Кроме того, функция события работает просто отлично, если вы используете их так, как они были предназначены для использования.
Данк
0

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

Сам механизм событий все еще можно использовать для событий в стиле "VB6", но вам придется изменить всех существующих потребителей, если вам когда-либо понадобится изменить сигнатуру (или, что еще хуже, создать новые версии тех же событий). При рекомендуемом подходе, поскольку более новые элементы наследуют от существующих, вы можете обновить подпись, не исправляя существующий код.

Марк Херд
источник