Apr 152009
 

Have you ever run into a situation where you need to bridge between code that uses the old custom EventHandler pattern for events and code that wants to use the new parameterized EventHandler<> code style? EventHandler style delegates are not assignment compatible with EventHandler<> delegates.  You’ll run into this if you’re writing a new API that sits on top of older code.  You probably want to expose events in your new API using the newer EventHandler<> pattern, but you can’t easily wire up events from the old lower level code to fire events in your new API.

You could create an event handler method to catch each of the old style events fired by the old code and turn around and fire the corresponding new style event in your API, but who wants to write all that mind numbingly repetitive code?

There is a simpler way.  The two incompatible event patterns do have one thing in common:  the Invoke method.

But first, some background.

Custom EventHandler Types, 1.0 Style

In .NET 1.0 the pattern to define an event property was to define an EventArgs class to carry the arguments of your event and define an EventHandler delegate type which uses that EventArgs class as its args parameter.  Like this:

    public class ActivityCompletedEventArgs : EventArgs
    {
        public ActivityCompletedEventArgs(int resultCode, string result)
        {
            ResultCode = resultCode;
            Result = result;
        }

        public int ResultCode { get; private set; }
        public string Result { get; private set; }
    }

    public delegate void ActivityCompletedEventHandler(object sender,
                                                       ActivityCompletedEventArgs e);

    class Bar
    {
        public event ActivityCompletedEventHandler Completed;
    }

Generic EventHandler<> Types, 2.0 Style

With the advent of generic types in .NET 2.0, the generic EventHandler<> parameterized type was born.  This parameterized EventHandler<> delegate type simplifies how you declare and use event handlers – you don’t have to declare a custom event handler delegate type to match your event args class every time you create a new kind of event.  This is exactly the kind of thing that parameterized types (aka generics) were created to solve. Like this:

    public class ActivityCompletedEventArgs : EventArgs
    {
        public ActivityCompletedEventArgs(int resultCode, string result)
        {
            ResultCode = resultCode;
            Result = result;
        }

        public int ResultCode { get; private set; }
        public string Result { get; private set; }
    }

    class Bar
    {
        public event EventHandler&lt;ActivityCompletedEventArgs&gt; Completed;
    }

However, the downside is that EventHandler<> delegates are not assignment compatible with the old style non-generic EventHandler style delegates.

The Invoke Bridge

Both styles of event delegate have an Invoke method with a signature of (Object sender, EventArgs args), where EventArgs is the arguments class specific to this particular event.  You can use this point of commonality to convert between event styles without too much trouble.  The trick is to use the .Invoke method in one style of event as the callback method registered in the other style of event:

    class OldStyleClass
    {
        public event ActivityCompletedEventHandler OldStyleEvent;
    }

    class NewStyleClass
    {
        public event EventHandler&lt;ActivityCompletedEventArgs&gt; Completed;

        public void blah()
        {
            var obj = new OldStyleClass();
            obj.OldStyleEvent += new ActivityCompletedEventHandler(Completed.Invoke);
        }
    }

The code above hooks up the events so that when OldStyleClass.OldStyleEvent fires, it will fire the NewStyleClass.Completed event.

The main catch to this technique (in C#) is that the reference to the event’s .Invoke method must be made from within the context of the class that defines the event. C# doesn’t allow access to the members of an event delegate outside of the class that defines the event. The rules may be different in other .NET languages.

If you want to use this technique in the opposite direction (to have a new style event fire an old style event), you will need to be able to modify the source code of the old style class that defines the old style event. This is much less common than making old style code to fire new style events.

Sorry, the comment form is closed at this time.