Ответ 1
Я хочу уточнить ответ BJ Myers (правильный).
В С# событие можно рассматривать как поле типа делегата - так же, как свойство можно рассматривать как поле типа свойства, а значение этого "поля" может быть нулевым. Если вы находитесь в неудачной ситуации, когда обработчик события изменяется на один поток, а другой поток пытается его вызвать, вы можете попасть в ситуацию, когда:
if (this.SomeEvent != null)
this.SomeEvent( ... );
не является потокобезопасным. Значение может быть мутировано таким образом, чтобы оно не было равно null перед проверкой, null после проверки и сбой программы.
Обычный способ сделать это "threadafe", и я использую этот термин, желательно скопировать значение в локальный, а затем проверить локальный для null. Это имеет преимущество не сбой с нулевым разыменованием. Тем не менее, умный разработчик заметит, что еще есть гонка! Последовательность может быть
- Обработчик ненулевого события, кэшированный в потоке A
- Обработчик событий установлен на нуль в потоке B
- Состояние, необходимое для обработчика событий, уничтожается в потоке B
- Обработчик событий работает в потоке A и ужасно умирает
Таким образом, в этом смысле этот шаблон не является "потокобезопасным". Если вы находитесь в этой неудачной позиции , вы отвечаете за то, что соответствующая логика потоков реализована так, что этого не может произойти. Вы можете сделать это, как хотите. Если вы хотите (сомнительные) преимущества иметь возможность вызывать обработчик события в одном потоке при мутации события в другом потоке, то вам нужно заплатить либо за безопасность, либо за дело с ошибками условий гонки.
Я лично избегал бы такой ситуации, как чума, но я недостаточно умен, чтобы написать правильный многопоточный код.
Теперь, что касается актуального вопроса:
some_expression ?. ToString();
совпадает с
temp = some_expression
temp == null ? null : temp.ToString()
Является ли последний код "потоковым" по вашему мнению?