单次活动订阅

我相信这是不可能的,但我会问。

为了对事件进行单次订阅,我经常发现自己使用这种(自我发明的)模式:

EventHandler handler=null; handler = (sender, e) => { SomeEvent -= handler; Initialize(); }; SomeEvent += handler; 

这是相当多的锅炉板,它也使得Resharper对改装后的瓶盖抱怨。 有没有办法将此模式转换为扩展方法或类似方法? 这样做的更好方法是什么?

理想情况下,我喜欢这样的东西:

 SomeEvent.OneShot(handler) 

重构扩展方法并不容易,因为在C#中引用事件的唯一方法是通过订阅( += )或取消订阅( -= )(除非它在当前类中声明)。

您可以使用与Reactive Extensions中相同的方法: Observable.FromEvent需要两个委托订阅该事件并取消订阅。 所以你可以这样做:

 public static class EventHelper { public static void SubscribeOneShot( Action subscribe, Action unsubscribe, EventHandler handler) { EventHandler actualHandler = null; actualHandler = (sender, e) => { unsubscribe(actualHandler); handler(sender, e); }; subscribe(actualHandler); } } ... Foo f = new Foo(); EventHelper.SubscribeOneShot( handler => f.Bar += handler, handler => f.Bar -= handler, (sender, e) => { /* whatever */ }); 

以下代码适用于我。 必须通过字符串指定事件并不完美,但我没有胶水如何解决这个问题。 我想这在当前的C#版本中是不可能的。

 using System; using System.Reflection; namespace TestProject { public delegate void MyEventHandler(object sender, EventArgs e); public class MyClass { public event MyEventHandler MyEvent; public void TriggerMyEvent() { if (MyEvent != null) { MyEvent(null, null); } else { Console.WriteLine("No event handler registered."); } } } public static class MyExt { public static void OneShot(this TA instance, string eventName, MyEventHandler handler) { EventInfo i = typeof (TA).GetEvent(eventName); MyEventHandler newHandler = null; newHandler = (sender, e) => { handler(sender, e); i.RemoveEventHandler(instance, newHandler); }; i.AddEventHandler(instance, newHandler); } } public class Program { static void Main(string[] args) { MyClass c = new MyClass(); c.OneShot("MyEvent",(sender,e) => Console.WriteLine("Handler executed.")); c.TriggerMyEvent(); c.TriggerMyEvent(); } } } 

我建议使用“自定义”事件,以便您可以访问调用列表,然后使用Interlocked.Exchange同时读取和清除调用列表来引发事件。 如果需要,可以使用简单的链表列表以线程安全的方式完成事件订阅/取消订阅/提升; 引发事件时,代码可以在Interlocked.Exchange之后反转堆栈项的顺序。 对于取消订阅方法,我可能建议只在invocation-list项中设置一个标志。 理论上,如果事件在没有引发事件的情况下重复订阅和取消订阅,则理论上可能导致内存泄漏,但这将使得非常简单的线程安全取消订阅方法成为可能。 如果想要避免内存泄漏,可以计算列表中仍有多少未订阅事件; 如果在尝试添加新的事件时列表中有太多未订阅的事件,则add方法可以通过列表并删除它们。 仍可在完全无锁的线程安全代码中使用,但更复杂。