Это лучший способ запускать/вызывать события без нулевой проверки на С#?

В большинстве случаев, которые я видел, используется следующий способ объявления и вызова запуска событий:

public class MyExample
{
    public event Action MyEvent; // could be an event EventHandler<EventArgs>, too

    private void OnMyEvent()
    {
        var handler = this.MyEvent; // copy before access (to aviod race cond.)
        if (handler != null)
        {
            handler();
        }
    }

    public void DoSomeThingsAndFireEvent() 
    {
        // ... doing some things here
        OnMyEvent();
    }
 }

Даже ReSharper генерирует метод вызова, упомянутый выше.

Почему бы просто не сделать так:

public class MyExample
{
    public event Action MyEvent = delegate {}; // init here, so it never null

    public void DoSomeThingsAndFireEvent() 
    {
        // ... doing some things here
        OnMyEvent(); // save to call directly because this can't be null
    }
 }

Может кто-нибудь объяснить причину, почему бы не сделать это? (против против)

Ответ 1

Плюсы и минусы:

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

  • Вы должны помнить, что всегда создавайте пустой делегат. Это действительно легче, чем вспомнить, что нужно проверить null?

  • Ни один из шаблонов не делает событие потокобезопасным. По-прежнему вполне возможно, что оба шаблона для обработчика событий могут быть запущены на одном потоке, а удаляются в другом потоке, а это означает, что они участвуют в гонке. Если код удаления обработчика разрушает состояние, требуемое обработчиком, возможно, что один поток уничтожает это состояние, а другой поток выполняет обработчик. Не думаю, что просто проверка на нуль или присваивание пустой обработчик волшебно устраняет условия гонки. Это только устраняет состояние гонки, что приводит к разыменования NULL.

Ответ 2

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

Ответ 3

Он все равно может быть нулевым. Рассмотрим:

    var x = new MyExample();
    x.MyEvent += SomeHandler;

    // ... later, when the above code is disposed of

    x.MyEvent -= SomeHandler;

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

Я не уверен, что это надежное поведение, или просто артефакт языковой реализации...