Ответ 1
Не злой. Я хочу, чтобы события работали таким образом по умолчанию. Может кто-нибудь объяснить, почему событие без подписчиков имеет значение null?
Зло или не зло?
public static void Raise(this EventHandler handler, object sender, EventArgs args)
{
if (handler != null)
{
handler(sender, args);
}
}
// Usage:
MyButtonClicked.Raise(this, EventArgs.Empty);
// This works too! Evil?
EventHandler handler = null;
handler.Raise(this, EVentArgs.Empty);
Обратите внимание, что из-за характера методов расширения MyButtonClicked.Raise не будет вызывать исключение NullReferenceException, если MyButtonClicked имеет значение NULL. (Например, нет слушателей события MyButtonClicked).
Зло или нет?
Не злой. Я хочу, чтобы события работали таким образом по умолчанию. Может кто-нибудь объяснить, почему событие без подписчиков имеет значение null?
Вы всегда можете объявлять свои события как это (не то, что я рекомендую):
public event EventHandler<EventArgs> OnClicked = delegate { };
Таким образом у них есть что-то, назначенное им, когда вы их вызываете, поэтому они не выбрасывают исключение нулевого указателя.
Вероятно, вы можете избавиться от ключевого слова delegate в С# 3.0...
Не забудьте использовать [MethodImpl(MethodImplOptions.NoInlining)]
, иначе возможно, что он не является потокобезопасным.
(Прочтите, что где-то давно, вспомнил об этом, googled и нашел http://blog.quantumbitdesigns.com/tag/events/)
Исходя из java-фона, это всегда казалось странным для меня. Я думаю, что никто, кто слушает событие, совершенно не действует. Особенно, когда слушатели добавляются и удаляются динамически.
Мне кажется, что это один из С# gottchas, который вызывает ошибки, когда люди не знают/забывают проверять значение null каждый раз.
Скрытие этой детали реализации кажется хорошим планом, поскольку это не помогает читабельности проверять нули каждый раз. Я уверен, что MSFTs скажут там прирост производительности, не создавая события, если никто не слушает, но imho значительно перевешивается исключениями бессмысленного нулевого указателя/уменьшением удобочитаемости в большинстве бизнес-кодов.
Я бы также добавил эти два метода в класс:
public static void Raise(this EventHandler handler, object sender)
{
Raise(handler, sender, EventArgs.Empty);
}
public static void Raise<TA>(this EventHandler<TA> handler, object sender, TA args)
where TA : EventArgs
{
if (handler != null)
{
handler(sender, args);
}
}
Почему это было бы зло?
Его цель ясна: он вызывает событие MyButtonClicked.
Он добавляет служебные вызовы функции, но в .NET он либо будет оптимизирован, либо довольно быстро в любом случае.
Это немного тривиально, но он исправляет мою самую большую жалобу на С#.
В целом, я думаю, что это фантастическая идея, и, вероятно, украсть ее.
Я бы не сказал это зло, но меня интересует, как ваш метод расширения вписывается в
protected virtual OnSomeEvent(EventArgs e){ }
и как он обрабатывает расширяемость через наследование. Предполагается ли, что все подклассы обрабатывают событие вместо переопределения метода?
Хотя я бы не назвал его злым, он все еще имеет отрицательную импликацию, поскольку он добавляет лишние накладные расходы:
При вызове
myEvent.Raise(this, new EventArgs());
объект EventArgs инициализируется во всех ситуациях, даже если никто не подписался на myEvent.
При использовании
if (myEvent!= null) {
myEvent(this, new EventArgs());
}
EventArgs инициализируется только в том случае, если кто-то подписался на myEvent.
Бросок исключения, когда нет обработчиков, на самом деле не является предпочтительным. Если у него нет обработчиков, лучше быть пустым, а не нулевым.