2009-10-22 8 views
11

Я просто читал страницу на events на 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; 

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

Я пропустил что-то очевидное здесь?

+1

Может ли RaiseCustomEvent установить значение null на другой поток, прежде чем у текущего метода есть возможность его запустить? –

ответ

17

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

+0

Ahh ok, что делает гораздо больше смысла, спасибо! Должен сказать, я впечатлен тем, как быстро это получилось! – David

4

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

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

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

1

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

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

0

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

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

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

Смежные вопросы