Потенциальный недостаток для запуска события?
Учебник по событиям в Microsoft показывает, как проверить event
для null
перед его запуском:
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
{ // Potential Race-condition at this point!
Changed(this, e);
}
}
Но это оставляет открытым состояние гонки, как описано в блоге Эрика Липперта, где он пишет, что события должны запускаться через локальное событие, чтобы избежать гоночного состояния:
protected virtual void OnChanged(EventArgs e)
{
ChangedEventHandler temp = Changed; // Local copy of the EventHandler
if (temp != null)
{ // Race condition avoided by local variable.
temp(this, e);
}
}
В то время как это работает, это сбило с толку многих разработчиков, которые ошибаются, и не помещают его в локальное событие.
Другим решением от DailyCoding является всегда инициализировать ваше событие, чтобы иметь один пустой обработчик, поэтому нулевой чек никогда не нужен:
// Set with empty delegate, will never be null
public event ChangedEventHandler Changed = delegate { };
protected virtual void OnChanged(EventArgs e)
{
// Neither Null-check nor local variable is needed; just trigger the event.
Changed(this, e);
}
Это имеет большой смысл и довольно просто.
Однако, так как я вижу, что этот метод так редко упоминается в Интернете, я думаю, что должна быть причина, почему.
Есть ли недостаток в инициализации события с пустым делегатом, подобным этому?
Ответы
Ответ 1
Вы увидите абсолютно крошечный удар производительности, но есть проблемы в более сложных случаях, например, сериализация и десериализация класса могут привести к потере фальшивого обработчика событий и отсутствию нулевой проверки, а затем выбросу исключения.
Ответ 2
- Это небольшое повышение производительности, если событие было пустым.
- Если вы когда-либо пишете
Changed = null
в классе, он сломается.
Ответ 3
В опубликованной в блоге статье Эрика Липперта он говорит:
Существуют другие способы решения этой проблемы; например, инициализация обработчик должен иметь пустое действие, которое никогда не удаляется. Но делать null check - это стандартный шаблон.
Но до этого он также говорит:
Удаление кода вокруг сайта вызова [нулевая проверка] не уменьшает количество условий гонки в коде [...]. Хуже того, это затрудняет определение условий гонки сокращение окна, в котором может произойти гонка без устранения он.
Это потому, что, как он описывает, это все равно может случиться
Между нажатием значения делегата [в стек] и вызовом для его вызова [...]
Итак, в основном, если вы используете пустой обработчик, вы испытываете некоторую потерю производительности (это, кажется, консенсус здесь). Таким образом, вы получаете то, что получаете, это читаемость, но самое главное: странное поведение будет более очевидным. (Я делаю это из-за меньшей производительности → занимает больше времени → с большей вероятностью вызывать устаревший обработчик)
Поэтому, если вы полностью осознаете, что такие вещи могут произойти, и нулевая проверка вас не беспокоит, идите на это. Или нет, если вы этого не хотите.