С#: инициализация обработчика событий с помощью манекена

Я видел такой код в некоторых местах:

public event SomeEventHandler SomeEvent = (s, e) => { };

Это рекомендуемый способ сделать что-то? Что он решает, и имеет ли он какие-либо примечательные побочные эффекты? Должен ли я все же делать нулевые проверки? Или это то, что мне больше не нужно делать? Будет ли сбор мусора работать по-прежнему?


Например:

private PropertyChangedEventHandler propertyChanged;
private readonly object propertyChangedLock = new object();
public event PropertyChangedEventHandler PropertyChanged
{
    add
    {
        lock (propertyChangedLock)
            propertyChanged += value;
    }
    remove
    {
        lock (propertyChanged)
            propertyChanged -= value;
    }
}
protected void OnPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler;
    lock (propertyChangedLock)
        handler = propertyChanged;

    if (handler != null)
        handler(this, new PropertyChangedEventArgs(propertyName));
}

Могу ли я изменить первую строку на это:

private PropertyChangedEventHandler propertyChanged = (s, e) => { };

А затем пропустите нулевую проверку в методе OnPropertyChanged? И если я затем пропущу нулевую проверку, я также могу пропустить блокировку? Если бы это дало бы мне это:

protected void OnPropertyChanged(string propertyName)
{
    propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

Будет ли это безопасно при учете инициализации? Или есть некоторые побочные эффекты, которые я пропустил?

Ответ 1

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

protected void OnPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler;
    lock (propertyChangedLock)
    {
        handler = propertyChanged;
    }
    handler(this, new PropertyChangedEventArgs(propertyName));
}

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

Лично я рекомендую вам не пытаться сделать потоки безопасными.

Ответ 2

Вы можете увидеть его как реализацию NULL Object pattern.

Это помогает сделать ваш код более удобочитаемым, поскольку вам не нужно выполнять проверки NULL.

Блоки в логике добавления/удаления должны остаться, если они понадобятся сейчас. Они не имеют к этому никакого отношения. Они используются, чтобы избежать условий гонки (но я не знаю, нужны ли они в вашей самой ситуации).