Я поднял ту же проблему около недели назад и пришел к противоположному выводу:
C # События и безопасность потоков
Ваше резюме ничего не делает, чтобы убедить меня в обратном!
Во-первых, клиенты класса не могут присвоить событию значение null. Вот и весь смысл ключевого слова event
. Без этого ключевого слова это было бы поле с делегатом. При этом все операции над ним являются частными, за исключением зачисления и исключения из списка.
В результате присвоение delegate {}
событию на этапе строительства полностью соответствует требованиям правильной реализации источника события.
Конечно, внутри класса может быть ошибка, когда событие установлено на null
. Однако в любом классе, который содержит поле любого типа, может быть ошибка, которая устанавливает для поля значение null
. Хотели бы вы, чтобы при каждом доступе к ЛЮБОМУ членскому полю класса мы писали такой код?
// field declaration:
private string customerName;
private void Foo()
{
string copyOfCustomerName = customerName;
if (copyOfCustomerName != null)
{
// Now we can use copyOfCustomerName safely...
}
}
Конечно, нет. Все программы стали бы вдвое длиннее и вдвое менее читабельными, без веской причины. Такое же безумие возникает, когда люди применяют это «решение» к событиям. События не являются общедоступными для назначения, так же, как и частные поля, и поэтому их можно использовать напрямую, если вы инициализируете их пустым делегатом при создании.
Единственная ситуация, в которой вы не можете сделать это, - это когда у вас есть событие в struct
, но это не совсем неудобство, так как события появляются на изменчивых объектах (указывая на изменение состояния) и struct
Общеизвестно, что если они мутируют, то они хитрые, поэтому лучше всего сделать их неизменяемыми, и, следовательно, события малопригодны для struct
s.
Может существовать другое совершенно отдельное состояние гонки, как я описал в своем вопросе: что, если клиент (приемник событий) хочет быть уверен, что его обработчик не будет вызван после того, как он был исключен из списка? Но, как отметил Эрик Липперт, ответственность за решение лежит на клиенте. Вкратце: невозможно гарантировать, что обработчик событий не будет вызван после его исключения из списка. Это неизбежное следствие неизменности делегатов. Это верно, независимо от того, вовлечены ли потоки.
В блоге Эрика Липперта он ссылается на мой SO вопрос, но затем заявляет другой, но похожий вопрос . Я думаю, он сделал это с законной риторической целью - просто для того, чтобы подготовить почву для обсуждения вопроса о вторичной гонке, которая влияет на организаторов события. Но, к сожалению, если вы перейдете по ссылке на мой вопрос, а затем немного небрежно прочитаете его пост в блоге, у вас может сложиться впечатление, что он отказывается от техники «пустой делегат».
На самом деле он говорит: «Есть и другие способы решения этой проблемы, например, инициализация обработчика для получения пустого действия, которое никогда не удаляется», что является техникой «пустой делегат».
Он охватывает «выполнение нулевой проверки», потому что это «стандартный шаблон»; мой вопрос был, почему это стандартная схема? Джон Скит предположил, что, учитывая, что совет предшествует добавлению анонимных функций в язык, это, вероятно, просто похмелье от C # версии 1, и я думаю, что это почти наверняка так, поэтому я принял его ответ.