在第一次使用后,我还没有找到取消订阅该事件的好方法。(您当然可以使用大量反射的方法,但我怀疑如果重构更改了事件的名称,编译器会抱怨)。
这是一个只使用委托的版本,所以编译器仍然可以很好地为您服务。它可能不像你需要的那么轻,但由于我接受了自己的启迪挑战,我想我会分享它。
MyEvent += SingleUseEventHandler<AssemblyLoadEventArgs, AssemblyLoadEventHandler>
.Create(This_MyEventOccurred);
这里定义了魔法:
public class SingleUseEventHandler<TArgs,THandler>
where TArgs : EventArgs
{
public static THandler Create(EventHandler<TArgs> handler)
{
var helper = new SingleUseEventHandler<TArgs, THandler>(handler);
EventHandler<TArgs> h = helper.InvokeIfFirstTime;
return (THandler)(object)Delegate.CreateDelegate(typeof(THandler), h.Target, h.Method);
}
public void InvokeIfFirstTime(object sender, TArgs args)
{
if (!raised)
{
raised = true;
handler(sender, args);
}
}
public SingleUseEventHandler(EventHandler<TArgs> handler)
{
this.handler = handler;
}
bool raised;
readonly EventHandler<TArgs> handler;
}
当然,C# 不会推断委托类型,因此您必须明确指定它。
如果事件的定义是 EventHandler,您可以使用它来代替:
MyEvent += SingleUseEventHandler<SomeEventArgs>.Create(SomeHandlerMethod);
public static class SingleUseEventHandler<TArgs>
where TArgs : EventArgs
{
public static EventHandler<TArgs> Create(EventHandler<TArgs> handler)
{
var helper = new SingleUseEventHandler<TArgs, EventHandler<TArgs>>(handler);
return helper.InvokeIfFirstTime;
}
}
这是一个示例程序:
class Program
{
static event AssemblyLoadEventHandler MyEvent;
static int callCount;
static void Main(string[] args)
{
MyEvent += SingleUseEventHandler<AssemblyLoadEventArgs, AssemblyLoadEventHandler>
.Create(Load);
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine("Raising event for " + assembly.GetName().Name);
MyEvent(null, new AssemblyLoadEventArgs(assembly));
}
}
static void Load(object sender, AssemblyLoadEventArgs eventArgs)
{
Console.WriteLine(++callCount);
}
}