Является ли вызов метода расширения для «нулевой» ссылки (то есть события без подписчиков) злым? - PullRequest
59 голосов
/ 29 октября 2008

зло или не зло?

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 ]

35 голосов
/ 29 октября 2008

Не зло. Я бы хотел, чтобы события работали таким образом по умолчанию. Может кто-нибудь объяснить, почему событие без подписчиков является нулевым?

14 голосов
/ 29 октября 2008

Вы всегда можете объявить свои события следующим образом (не то, чтобы я рекомендовал это):

public event EventHandler<EventArgs> OnClicked = delegate { };

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

Вероятно, вы можете избавиться от ключевого слова делегата в C # 3.0 ...

9 голосов
/ 16 ноября 2008

Не забудьте использовать [MethodImpl(MethodImplOptions.NoInlining)], иначе возможно, что он не является потокобезопасным.

(прочитал это где-то давно, вспомнил, погуглил и нашел http://blog.quantumbitdesigns.com/tag/events/)

6 голосов
/ 11 ноября 2008

Исходя из Java-фона, это всегда казалось мне странным. Я думаю, что никто не слушает событие совершенно правильно. Особенно, когда слушатели добавляются и удаляются динамически.

Мне кажется, это одна из ошибок C #, которая вызывает ошибки, когда люди не знают / не забывают каждый раз проверять на ноль.

Сокрытие этой детали реализации кажется хорошим планом, поскольку она не помогает читабельности проверять наличие нулей каждый раз. Я уверен, что MSFT скажут, что есть выигрыш в производительности, не создавая событие, если никто не слушает, но 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);
        }
    }
5 голосов
/ 29 октября 2008

Почему это было бы зло?

Его цель ясна: он вызывает событие MyButtonClicked.

Он добавляет накладные расходы на вызов функции, но в .NET он либо будет оптимизирован, либо все равно будет довольно быстрым.

Это немного тривиально, но это исправляет мою самую большую жалобу с C #.

В целом, я думаю, что это фантастическая идея, и, вероятно, украду ее.

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

Создание исключения, когда нет обработчиков, на самом деле не является предпочтительным для большинства. Если у него нет обработчиков, лучше быть пустым, чем нулевым.

0 голосов
/ 18 февраля 2010

Хотя я бы не охарактеризовал его как зло , он по-прежнему имеет отрицательное значение, поскольку добавляет ненужные накладные расходы:

При звонке

myEvent.Raise(this, new EventArgs());

объект EventArgs инициализируется во всех ситуациях, даже если никто не подписан на myEvent.

При использовании

if (myEvent!= null) {
   myEvent(this, new EventArgs());
}

EventArgs инициализируется, только если кто-то подписался на myEvent.

0 голосов
/ 29 октября 2008

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

protected virtual OnSomeEvent(EventArgs e){ }

шаблон и как он обрабатывает расширяемость с помощью наследования. Предполагается ли, что все подклассы будут обрабатывать событие вместо переопределения метода?

...