Копирование делегатов - PullRequest
       11

Копирование делегатов

11 голосов
/ 22 октября 2009

Я просто читал страницу о событиях в MSDN, и я натолкнулся на фрагмент кода примера, который озадачивает меня.

Код, о котором идет речь, такой:

// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;

Я понимаю намерения кода, но я не вижу, как именно эта строка делает копию чего-либо. Все, что он делает, это копирует ссылку ; на самом деле он не делает глубокую копию экземпляра делегата. Таким образом, на самом деле это вовсе не мешает состоянию гонки.

Я что-то упускаю здесь очевидное?

Ответы [ 5 ]

18 голосов
/ 22 октября 2009

Делегаты являются неизменными, поэтому ссылка, полученная в этом коде, гарантированно не изменится.Если пользователь подписывается или отписывается после нулевой проверки, новый делегат будет создан и настроен на событие.Однако, поскольку у вас есть ссылка на совершенно другой объект, и вы вызываете его, вам не нужно беспокоиться о том, что он имеет значение null.

5 голосов
/ 22 октября 2009

Вы правы; это копирование ссылки.

Однако делегаты неизменны; когда вы добавляете обработчик к событию, создается новый делегат, объединяющий текущий обработчик (и) с новым, и затем назначаемый полю.

Экземпляр Delegate, на который ссылается поле, не может измениться, поэтому он избегает условия гонки.

3 голосов
/ 22 октября 2009

Эрик Липперт уже описал это в очень подробном посте .

1 голос
/ 22 октября 2009

Это тоже из MSDN ..

"Список вызовов делегата - это упорядоченный набор делегатов, в котором каждый элемент списка вызывает ровно один из методов, представленных делегатом. Список вызовов может содержать дубликаты методов. вызовы, методы вызываются в том порядке, в котором они появляются в списке вызовов. Делегат пытается вызвать каждый метод в своем списке вызовов; дубликаты вызываются один раз для каждого раза, когда они появляются в списке вызовов. Делегаты являются неизменными; после создания список вызовов делегата не изменяется."

0 голосов
/ 22 октября 2009

if (whatever != null) whatever(); выглядит так, как будто это гарантирует, что whatever никогда не будет нулевым при вызове whatever(), но на самом деле это не гарантирует этого в многопоточном сценарии. Другой поток может установить whatever = null между проверкой и вызовом.

Foo temp = whatever;
if (temp != null) temp();

Этот код исключает возможность разыменования с нулевым значением, поскольку temp является локальным и поэтому никогда не будет изменен другим потоком. Так что это предотвращает состояние гонки. Это не мешает всем соответствующим условиям гонки. Эрик Липперт провел более детальное обсуждение некоторых других проблем с кодом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...