Обновить. Для всех, кто это читает, начиная с .NET 4, блокировка не нужна из-за изменений в синхронизации автоматически генерируемых событий, поэтому я просто использую это сейчас:
public static void Raise<T>(this EventHandler<T> handler, object sender, T e) where T : EventArgs
{
if (handler != null)
{
handler(sender, e);
}
}
И поднять его:
SomeEvent.Raise(this, new FooEventArgs());
Прочитав одну из статей Jon Skeet статей по многопоточности, я попытался инкапсулировать подход, который он защищает, чтобы поднять событие в метод расширения, подобный этому (с аналогичной общей версией):
public static void Raise(this EventHandler handler, object @lock, object sender, EventArgs e)
{
EventHandler handlerCopy;
lock (@lock)
{
handlerCopy = handler;
}
if (handlerCopy != null)
{
handlerCopy(sender, e);
}
}
Затем это можно вызвать так:
protected virtual void OnSomeEvent(EventArgs e)
{
this.someEvent.Raise(this.eventLock, this, e);
}
Есть ли проблемы с этим?
Кроме того, я немного запутался в необходимости блокировки в первую очередь. Как я понимаю, делегат копируется в примере в статье, чтобы избежать возможности его изменения (и обнуления) между нулевой проверкой и вызовом делегата. Тем не менее, у меня создалось впечатление, что доступ/назначение такого типа является атомарным, поэтому зачем нужна блокировка?
Обновление: Что касается комментария Марка Симпсона ниже, я собрал тест:
static class Program
{
private static Action foo;
private static Action bar;
private static Action test;
static void Main(string[] args)
{
foo = () => Console.WriteLine("Foo");
bar = () => Console.WriteLine("Bar");
test += foo;
test += bar;
test.Test();
Console.ReadKey(true);
}
public static void Test(this Action action)
{
action();
test -= foo;
Console.WriteLine();
action();
}
}
Выводится:
Foo
Bar
Foo
Bar
Это показывает, что параметр делегата для метода (action
) не отражает аргумент, который был передан в него (test
), который, как я думаю, является ожидаемым. Мой вопрос в том, повлияет ли это на действительность блокировки в контексте моего метода расширения Raise
?
Обновление: Вот код, который я сейчас использую. Это не так элегантно, как мне бы хотелось, но, похоже, это работает:
public static void Raise<T>(this object sender, ref EventHandler<T> handler, object eventLock, T e) where T : EventArgs
{
EventHandler<T> copy;
lock (eventLock)
{
copy = handler;
}
if (copy != null)
{
copy(sender, e);
}
}