Introduction

If you wanted to record every event generated by any object, how would you do that? This article describes what I think is a new (if only slightly different) method
to allow one common point to handle any event.

Why?

Why would you want to handle multiple events from a single point? Lots of reasons, mine being that I was writing an application to test the field strength
of the 3G and 4G wireless mobile networks at certain locations.

A single point was needed to consolidate the data from each source and record it within a common data structure. Because of the nature of real-world programming,
each module had been developed previously for different purposes, and I had very little time to implement the application,
so re-writing each module to use a common event structure was not practical.

Background

I must begin this with a tip of the hat to the following article that served as inspiration: commoneventhandler.aspx.
This article covers an excellent method for attaching to any event without first knowing the signature of that event.

The reason I have created my own method, and subsequent article was that the above method did not quite serve my purposes, and I believe I have come up with
perhaps a new (if only slightly different) solution to this problem.

The crux of the issue: How can I have a single point in my code that can handle events from any class?

The main problem is that while Microsoft has tried to instill a common base for all event handlers with the signature
of (Object sender, EventArgs e) - this is not by any means mandatory. To bind a delegate to an event, no matter what,
the parameters of the Invoke method of the delegate must match those of the event handler delegate.

Another issue arises - suppose you do manage to bind multiple events to the same handler (to record or re-route those events) - the event handler doesn't necessarily
include any information about the event. In normal practices, you should know exactly what event is being raised, because you are only handling
the one event in each handler. However, using a common event means that one event handler is dealing with multiple events and event sources,
and it would really help if you knew what that event was.

An event handler's method signature must match the event-handler delegate, so there isn't any scope to include the event information in the method
signature (unless you can dictate the signature of the event handler, which we can't).

Also, I carefully investigated Reflection, and while there seems to be a way to get the calling method signature, there isn't any functionality to find the calling event.

The solution turned out to be found in putting an event-handler method on its own class, and setting the event-information as a property of that class.

This is the default implementation of an event-router class for the "EventHandler" standard delegate:

The event-info is passed in to the class with its constructor. The HandleEvent method can now pass that event-info structure on to the common event broker, along with
the sender of the event, and an array of objects containing the parameters.

// submit the event data to the common event broker.
CommonEventBroker.SubmitEvent(sender, Info, args);

By generating a new instance of this class for each event, then having that class' event-handler method raise a common event, passing in the event-info,
and arguments of the source event, we get the ability to distinguish between event types in the common event handler.

At this point you might be thinking, Yes that's great but it isn't very universal, is it?

And you'd be right. Time for the next part of the issue: how do we get the one event-handler method to bind to any event?

Other solutions all seem to fall back to using MSIL and Reflection.Emit to generate an event-handler signature at run time.

Effectively, this writes a new class and method to handle each type of event. My problem with this has been that the MSIL generated for these
event handlers is completely unintelligible - it is only one step away from assembly really.

Not only do I not want to deal with IL directly (it was never meant to be human coded), I don't want to burden future programmers that might have
to deal with my code, with having to read and understand that stuff.

What would be cool, would be if the event handler could be written
in C# at runtime, and compiled and attached to events as necessary. Only the method signature, class-name, and a couple of lines of code need to differ from a common template.

Turns out this is more than possible. The CodeDomProvider within the System.CodeDom namespace has the method:

I marked the spots where the code needs to be adjusted using %%, and used the EventHandlerType property of the EventInfo structure
to build the replacement code sections.

The generated code is then compiled to an in-memory assembly, and stored in a dictionary against the event-handler-type.
A new instance of this class can be used for each event that uses the same event handler type.

Binding an Event:

///<summary>/// subscribes the specified event from the specified object to the universal event broker.
/// when the event is raised, it will be rerouted to the CommonEventBroker.CommonEvent
///</summary>///<paramname="obj"></param>///<paramname="eventName"></param>publicstaticvoid Subscribe(object obj, string eventName)
{
// get the event-info:
EventInfo info = obj.GetType().GetEvent(eventName);
// get the event-handler:
var eventHandler = CreateEventHandler(info);
// attach the event handler to the source object:
eventHandler.AttachToEventOn(obj);
}

The subscribe method of the CommonEventBroker class subscribes to a named event on that class.

From that point, whenever that event is raised, the common event in the CommonEventBroker class will also be raised. The common event will have the event source,
event info, and parameters associated with it.

This is the delegate for the common event:

///<summary>/// delegate for a universal event handler. passes the source object,
/// the event-info, and all the parameters of the event.
///</summary>///<paramname="source"></param>///<paramname="eventInfo"></param>///<paramname="arguments"></param>publicdelegatevoid CommonEventHandler(object source, EventInfo eventInfo,
CommonEventParameter[] parameters);

Using the Code

In the example project, the form "Test" has a web-browser control hosted on it, and all events from the form and the browser are handled
by the common-event broker, with the following lines of code:

Points of Interest

When an event is raised by any class, the thread that raises the event is also the thread that must execute the event handler method.
Thus, attaching complex event handlers may introduce delays, or make an application unresponsive. If you are recording lots of events with
a single event handler, this problem is exacerbated.

To get around this I am using a separate thread to process events. The SubmitEvent method of CommonEventBroker adds
the event details to a FIFO queue, sets an AutoResetEvent, then returns control back to the source of the event. The consumer thread is signaled
by the AutoResetEvent then de-queues the event data and raises the CommonEvent. The code within the common event handler is then executed
by the consumer thread, and not the thread that raised the event. This decouples the event and its processing, meaning that you can write very heavy processing code
for the event without sacrificing responsiveness.

Stuff I Could Have Done Better

I'm sure there is heaps, I didn't have time to use best practices all the way through. Mainly, the CommonEventHandler delegate should use only two arguments,
and the second argument should be a derivation of EventArgs.

Also I made the CommonEventBroker static. This limits you to one common event. I could have used the Singleton pattern instead.

Latest Updates

Firstly, my thanks to those that have left positive comments about this article. I hope it has been useful.

I have made a few upgrades to the code that may offer increased usability. Mainly, I have changed the event-broker from a static class called CommonEventBroker
to an instance class called EventBroker. This class now has a static property called CommonEventBroker, much like the Singleton pattern, but unlike Singleton,
the constructor is still public so you can create instances of the class. I feel this gives the best of both worlds: there is a truly common, static instance of the broker,
as well as specific instances.

The test form has been updated to utilize its own instance of the event-broker.

In addition, I have changed the signature of the common-event delegate to match best-practices, it now has two parameters: the first being the sender,
and the second being a derivation of EventArgs.

I have also set the event-broker class to implement the IDisposable interface, which will stop the event processing thread in the Dispose() method.

Share

About the Author

Wrote his first computer game in Microsoft Basic, on a Dragon 32 at age 7. It wasn't very good.
Has been working as a consultant and developer for the last 15 years,
Discovered C# a few years ago and hasn't looked back.
Feels wierd talking about himself in the third person.

This is really useful and nicely done. You should really check out Roslyn as Sacha noted.
I also wrote a Math parser based on CodeDomProvider and was disappointed by the performance. Roslyn seems to be superior and easier to handle (instead of a gigantic string you are working with OO design)!
Again - great work!

Great article. Obviously, this is not something that most of us would us every day, but it does provide a lot of code examples, explains some of the underlying architecture of how events/delegates work, and provides a good solution for an uncommon problem.

Did you consider other solutions to your initial problem? I have in mind, for example, the interception capabilities provided out of the box by DI frameworks such as Spring.NET, Castle Windsor and Unity. I'm just not convinced that you're doing any more than re-inventing the wheel.

When you have less than a day to implement a solution to a problem, you dont look past one that works.
This is what programming is in the real world.
I had not seen a solution that achieved the same functionality without using MSIL, so I decided to use my free time to share the solution with the community - I didnt do it to get votes.
I may have reinvented the wheel but I didnt claim otherwise. Maybe read the title of the article.
Im not familiar with the products you mention, but I dislike using 3rd party add ons. They often come with usage restrictions, or cost money. Besides, if I had used a third party library it wouldnt really be my solution, and anyone else wanting to use it would also inherit the onus of using said library.