Generic firing of asynchoronous events

time to read 3 min | 552 words

Tomer Gabel had a problem with raising events from outside the class that declared them. The problem was compounded by the need to process the event asynchoronously, since System.Delegate doesn't implement BeginInvoke().

His solution was to use Reflection to do that. I was interested enough to investigate whatever I could do that without Reflection. I was surprised to learn that BeginInvoke was only available on specialized delegates. My solution is based around this article: Calling Synchronous Methods Asynchronously which truly proves that you can solve anything in computers by putting another layer of indirection, except too many layers of indirection :-)

Since Tomer seems to want a contain to hold many events, I decided that I would skip events and go straight to the source, delegates. The container class that I've here has a list of registered events and list of subscribers. Subscribing to a non-existing method creates it. Firing events can be done in three modes:

  • Totally synchoronously, which means that you wait until all the subscribers have finished.
  • Asynchoronously firing the event, but each subscriber is processed in turn.
  • Asynchoronously firing the event, each subscriber is proccessed asynchoronously.

Note: Exceptions during calls is a tricky subject, I tried to give as much information as possible, but I've no idea how to get an exception thrown from async method.

There is no reflection there, but a lot of indirection, the whole thing is based around the concept that while you may not be able to run a Delegate asyncrously, you can run a method asyncrously that would run the delegate. If you managed to follow the last sentence, I applaud you.

private delegate void DynamicInvoke(Delegate d, object[] args);

public
 void BeginDynamicInvoke(Delegate call, object[] args, EventHandler callback)
{
 this.fireSignle = new DynamicInvoke(CallDynamicInvoke);
 this.fireSignle.BeginInvoke(call, args, new AsyncCallback(AsyncCallback), null);
}

private
 void CallDynamicInvoke(Delegate d, object[] args)
{
 d.DynamicInvoke(args);
}

It's too big to post fully on the blog, you can find it here.

Unsubscribing and deleting events are left as an exercise to the reader :-)