Является ли вызов метода расширения на "нулевой" ссылке (т. е. событие без подписчиков) злом?
зло или не зло?
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).
зло или нет?
8 ответов:
Не зло. Я хочу, чтобы события работали таким образом по умолчанию. Может ли кто-нибудь объяснить, почему событие без подписчиков равно null?
вы всегда можете объявить ваши события, как это (не то, что я рекомендую его):
public event EventHandler<EventArgs> OnClicked = delegate { };
таким образом, у них есть что-то назначенное им при вызове, поэтому они не бросают исключение нулевого указателя.
вы, вероятно, можете избавиться от ключевого слова делегата в C# 3.0...
Не забудьте использовать
[MethodImpl(MethodImplOptions.NoInlining)]
, иначе возможно, что это не потокобезопасно.(читал, что где-то давно, вспомнил, погуглил и нашел http://blog.quantumbitdesigns.com/tag/events/ )
исходя из фона java, это всегда казалось мне странным. Я думаю, что никто не слушает событие совершенно справедливо. Особенно когда слушатели добавляются и удаляются динамически.
Мне кажется, что это один из gottchas C#, который вызывает ошибки, когда люди не знают / забывают проверять null каждый раз.
скрытие этой детали реализации кажется хорошим планом, так как это не помогает читаемости проверять значения 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 он будет либо оптимизирован, либо довольно быстро в любом случае.
Это немного тривиально, но это исправляет мою самую большую жалобу с C#.
в целом, я думаю, что это фантастическая идея, и, вероятно, украл его.
Я бы не сказал, что это зло, но мне интересно, как ваш метод расширения вписывается в
protected virtual OnSomeEvent(EventArgs e){ }
шаблон и как он обрабатывает расширяемость через наследование. Предполагается ли, что все подклассы будут обрабатывать событие вместо переопределения метода?
хотя я бы не описал его как зло, это все еще имеет отрицательный подтекст, так как он добавляет ненужные накладные расходы:
при вызове
myEvent.Raise(this, new EventArgs());
объект EventArgs инициализируется во всех ситуациях, даже если никто не подписался на myEvent.
при использовании
if (myEvent!= null) { myEvent(this, new EventArgs()); }
EventArgs инициализируется только в том случае, если кто-то подписался на myEvent.