Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One shot events using Lambda in C#

Tags:

c#

lambda

events

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?

like image 945
AnthonyWJones Avatar asked Jan 27 '10 21:01

AnthonyWJones


2 Answers

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);     } } 
like image 125
dtb Avatar answered Sep 17 '22 20:09

dtb


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!")); 
like image 44
Jordão Avatar answered Sep 16 '22 20:09

Jordão