class SomeClass
{
// Доступ к данному событию происходит в нескольких потоках.
public event EventHandler SomeEvent;
}
Стандартный, потоко-безопасный (thread safe) шаблон вызова события выглядит следующим образом:
// Потоко-безопасный вызов.
public void OnSomeEventThreadSafe(EventArgs args)
{
// Очень важно, сделать копию события:
EventHandler handler = SomeEvent;
// И далее работать только с ней:
if (handler != null)
{
handler(this, args);
}
}
Зачем, нам работать с копией, если мы можем и с оригиналом? Если коротко, то при многопоточном доступе к событию SomeEvent, у нас может возникнуть исключение. Например, если второй поток удалит последний зарегистрированный delegate из SomeEvent, в то время как в первом потоке проверка SomeEvent!=null была успешно пройдена.
// Не потоко-безопасный вызов.
void OnSomeEvent(EventArgs args)
{
if (SomeEvent != null)
{
// SomeEvent - может оказаться равным null.
SomeEvent(this, args);
}
}
Видно, что при добавлении нового события, при потоко-безопасном подходе приходится дублировать один и тот же кусок кода (копирование-проверка-вызов). Данное дублирование можно избежать, если вынести всю логику в метод-расширение (Extension Methods):
static class EventHandlerExt
{
// Метод расширение, для организации потоко-безопасного вызова события.
public static void Raise(
this EventHandler self, object sender, EventArgs args)
{
EventHandler handler = self;
if (handler != null)
{
handler(sender, args);
}
}
}
Использование:
SomeEvent.Raise(this, EventArgs.Empty);