I find myself doing this sort of thing quite often:-
EventHandler eh = null; //can't assign lambda directly since it uses eh eh = (s, args) => { //small snippet of code here ((SomeType)s).SomeEvent -= eh; } variableOfSomeType.SomeEvent += eh;
Basically I only want to attach an event handler to listen for one shot from the event, I no longer want to stay attached after that. Quite often that "snippert of code" is just one line.
My mind is going a bit numb, I'm sure there must be something I can do so I don't need to repeat all this overhead. Bear in mind that EventHandler
may well be EventHandler<T>
.
Any ideas how I can tidy up the repeative part of the code and just leave the snippet in a Lambda?
You could attache a permanent event handler to the event. The event handler then invokes "one shot event handlers" that are added to an internal queue:
OneShotHandlerQueue<EventArgs> queue = new OneShotHandlerQueue<EventArgs>(); Test test = new Test(); // attach permanent event handler test.Done += queue.Handle; // add a "one shot" event handler queue.Add((sender, e) => Console.WriteLine(e)); test.Start(); // add another "one shot" event handler queue.Add((sender, e) => Console.WriteLine(e)); test.Start();
Code:
class OneShotHandlerQueue<TEventArgs> where TEventArgs : EventArgs { private ConcurrentQueue<EventHandler<TEventArgs>> queue; public OneShotHandlerQueue() { this.queue = new ConcurrentQueue<EventHandler<TEventArgs>>(); } public void Handle(object sender, TEventArgs e) { EventHandler<TEventArgs> handler; if (this.queue.TryDequeue(out handler) && (handler != null)) handler(sender, e); } public void Add(EventHandler<TEventArgs> handler) { this.queue.Enqueue(handler); } }
Test class:
class Test { public event EventHandler Done; public void Start() { this.OnDone(new EventArgs()); } protected virtual void OnDone(EventArgs e) { EventHandler handler = this.Done; if (handler != null) handler(this, e); } }
You can use reflection:
public static class Listener { public static void ListenOnce(this object eventSource, string eventName, EventHandler handler) { var eventInfo = eventSource.GetType().GetEvent(eventName); EventHandler internalHandler = null; internalHandler = (src, args) => { eventInfo.RemoveEventHandler(eventSource, internalHandler); handler(src, args); }; eventInfo.AddEventHandler(eventSource, internalHandler); } public static void ListenOnce<TEventArgs>(this object eventSource, string eventName, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs { var eventInfo = eventSource.GetType().GetEvent(eventName); EventHandler<TEventArgs> internalHandler = null; internalHandler = (src, args) => { eventInfo.RemoveEventHandler(eventSource, internalHandler); handler(src, args); }; eventInfo.AddEventHandler(eventSource, internalHandler); } }
Use it like so:
variableOfSomeType.ListenOnce("SomeEvent", (s, args) => Console.WriteLine("I should print only once!")); variableOfSomeType.ListenOnce<InterestingEventArgs>("SomeOtherEvent", (s, args) => Console.WriteLine("I should print only once!"));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With