Обратите внимание, что делегаты являются ссылочными типами, и их значение по умолчанию равно нулю.
Решение, предложенное другими, то есть проверка на нулевое значение до запуска события, не является поточно-ориентированным, поскольку слушатели могут отписаться от события между проверкой на нулевое значение и активацией события.
Я видел решения, которые включают копирование делегата в локальную переменную и проверка его на нулевое значение до запуска, например
EventHandler myCustomEventCopy = MyCustomEvent;
if (myCustomEventCopy != null)
{
myCustomEventCopy (this, someArgs);
}
Но это имеет гоночное состояние, то есть обработчики могут запускаться даже после того, как они отписались от события, что может повредить состояние приложения.
Одним из решений, которое решает обе проблемы, является инициализация событий пустым обработчиком, например
public event EventHandler MyCustomEvent = delegate { };
и затем запустить их без каких-либо проверок, например
MyCustomEvent(this, someArgs);
Редактировать: Как уже отмечали другие, это сложная проблема.
http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx
Липперт отмечает, что для полного устранения проблемы «обработчик срабатывает после отмены регистрации» необходимо, чтобы сами обработчики 1020 * были написаны надежным образом.