В настоящее время "Избегайте проверки для обработчиков событий" в верхней части ответов на сообщение под заголовком Скрытые возможности С# и содержит очень вводящую в заблуждение информацию.
Хотя я понимаю, что Qaru - это "демократия", и ответ поднялся на вершину благодаря публичному голосованию, я чувствую, что многие люди, которые голосовали за ответ, либо не имели полного понимания С#/.NET или не нашли времени, чтобы полностью понять последствия практики, описанной в сообщении.
Короче говоря, пост защищает использование следующей конструкции, чтобы избежать необходимости проверять значение null при вызове события.
public event EventHandler SomeEvent = delegate {};
// Later..
void DoSomething()
{
// Invoke SomeEvent without having to check for null reference
SomeEvent(this, EventArgs.Empty);
}
На первый взгляд это может показаться умным ярлыком, но это может быть причиной серьезных головных болей в большом приложении, особенно если задействован concurrency.
Перед вызовом делегата события вы должны проверить нулевую ссылку. Просто потому, что вы инициализировали событие пустым делегатом, это не означает, что пользователь вашего класса не установит его в нуль в какой-то момент и сломает ваш код.
Что-то вроде этого типично:
void DoSomething()
{
if(SomeEvent != null)
SomeEvent(this, EventArgs.Empty);
}
Но даже в приведенном выше примере существует вероятность того, что в то время как DoSomething() может выполняться потоком, другой может удалить обработчики событий, и может возникнуть состояние гонки.
Предположим, что этот сценарий:
Thread A. Thread B. ------------------------------------------------------------------------- 0: if(SomeEvent != null) 1: { // remove all handlers of SomeEvent 2: SomeEvent(this, EventArgs.Empty); 3: }
В потоке B удаляются обработчики событий из события SomeEvent после того, как код, который вызывает событие, проверил делегат для нулевой ссылки, но до того, как он вызвал делегат. Когда SomeEvent (это EventArgs.Empty); вызов выполняется, SomeEvent имеет значение null и возникает исключение.
Чтобы избежать этой ситуации, лучшим примером для создания событий является следующее:
void DoSomething()
{
EventHandler handler = SomeEvent;
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
Для подробного обсуждения темы EventHandlers в .NET я предлагаю прочитать " Руководство по разработке каркаса" Кшиштофа Квалины и Брэда Абрамса, Глава 5, Раздел 4 - Дизайн событий. Особенно обсуждались темы Эрика Гуннерсона и Джо Даффи.
Как было предложено Эриком, в одном из приведенных ниже ответов я должен указать, что может быть разработано лучшее решение для синхронизации, которое позаботится об этой проблеме. Моя цель с этой должности состояла в том, чтобы повысить уровень осведомленности и , чтобы не дать решение с единственным и единственным истинным решением проблемы. Как предложил Эрик Липперт и Эрик Гуннерсон в вышеупомянутой книге, конкретное решение проблемы зависит от программиста, но важно то, что проблема не игнорируется.
Надеюсь, модератор аннотирует ответ в вопросе, так что ничего не подозревающие читатели не будут введены в заблуждение по плохой схеме.