Проверка на нуль до отправки события … streamобезопасная?

Что-то меня смущает, но никогда не вызывало никаких проблем … Рекомендуемый способ отправки мероприятия:

public event EventHandler SomeEvent; ... { .... if(SomeEvent!=null)SomeEvent(); } 

В многопоточной среде, как этот код гарантирует, что другой stream не изменит список вызовов SomeEvent между проверкой на null и вызовом события?

В C # 6.0 вы можете использовать монадический оператор Null-условный оператор ?. для проверки событий с нулевым и повышающим эффектом простым и streamобезопасным способом.

 SomeEvent?.Invoke(this, args); 

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

Как вы указываете, когда несколько streamов могут одновременно обращаться к SomeEvent , один stream может проверить, является ли SomeEvent нулевым, и определить, что это не так. После этого другой stream может удалить последний зарегистрированный делегат из SomeEvent . Когда первый stream пытается поднять SomeEvent , будет SomeEvent исключение. Разумным способом избежать этого сценария является:

 protected virtual void OnSomeEvent(EventArgs args) { EventHandler ev = SomeEvent; if (ev != null) ev(this, args); } 

Это работает, потому что всякий раз, когда делегат добавляется или удаляется из события с использованием реализаций по умолчанию для аксессуаров добавления и удаления, используются статические методы Delegate.Combine и Delegate.Remove. Каждый из этих методов возвращает новый экземпляр делегата, а не модифицирует переданный ему.

Кроме того, назначение объектной ссылки в .NET является атомарным , а реализации объектов добавления и удаления событий по умолчанию синхронизируются . Таким образом, код выше успешно завершается первым копированием делегата многоадресной передачи из события во временную переменную. Любые изменения в SomeEvent после этого момента не повлияют на копию, которую вы сделали и сохранили. Таким образом, вы можете теперь безопасно проверить, были ли зарегистрированы какие-либо delegates и впоследствии вызывать их.

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

Например, если обработчик события зависит от состояния, которое было уничтожено, как только обработчик не подписан, тогда это решение может вызвать код, который не может работать должным образом. См . Превосходную запись блога Эрика Липперта для более подробной информации. Также см. Этот вопрос и ответы StackOverflow .

EDIT: Если вы используете C # 6.0, то ответ Krzysztof выглядит как хороший способ.

Самый простой способ удалить эту нулевую проверку – назначить обработчик событий анонимному делегату. Штраф понесенный очень мало и освобождает вас от всех нулевых чеков, условий гонки и т. Д.

public event EventHandler SomeEvent = delegate {};

Связанный вопрос: Есть ли недостаток в добавлении анонимного пустого делегата в объявление события?

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

 EventHandler tmpEvent = SomeEvent; if (tmpEvent != null) { tmpEvent(); } 

Более безопасный подход:

public class Test { private EventHandler myEvent; private object eventLock = new object(); private void OnMyEvent() { EventHandler handler; lock(this.eventLock) { handler = this.myEvent; } if (handler != null) { handler(this, EventArgs.Empty); } } public event MyEvent { add { lock(this.eventLock) { this.myEvent += value; } } remove { lock(this.eventLock) { this.myEvent -= value; } } } }
public class Test { private EventHandler myEvent; private object eventLock = new object(); private void OnMyEvent() { EventHandler handler; lock(this.eventLock) { handler = this.myEvent; } if (handler != null) { handler(this, EventArgs.Empty); } } public event MyEvent { add { lock(this.eventLock) { this.myEvent += value; } } remove { lock(this.eventLock) { this.myEvent -= value; } } } } 

-законопроект

Я хотел бы предложить небольшое улучшение ответа RoadWarrior, используя функцию расширения для EventHandler:

 public static class Extensions { public static void Raise(this EventHandler e, object sender, EventArgs args = null) { var e1 = e; if (e1 != null) { if (args == null) args = new EventArgs(); e1(sender, args); } } } 

С этим расширением в масштабах событий события могут быть подняты просто:

class SomeClass {публичное событие EventHandler MyEvent;

 void SomeFunction() { // code ... //--------------------------- MyEvent.Raise(this); //--------------------------- } 

}

События c #

  • Глобальные события в угловых
  • Должен ли я всегда отключать обработчики событий в методе Dispose?
  • Что означает «Захват мыши» в WPF?
  • Когда используется пул streamов?
  • + = new EventHandler (метод) vs + = Метод
  • Как добавить событие в UserControl в C #?
  • Как захватить щелчок мышью по элементу в ListBox в WPF?
  • Что такое WPF Preview Events?
  • CKEDITOR - как добавить постоянное событие onclick?
  • Каковы различия между делегатами и событиями?
  • Как я могу отменить регистрацию «анонимного» обработчика событий
  • Давайте будем гением компьютера.