Wrapping delegates in C#

Monday 28 March 2005

Continuing my education in C#, I don't understand what happens to exceptions in event handlers. For the most part, when I register an event handler for a UI event (like button click), if the handler throws an exception, I get a detailed dialog box showing what happened. But for some events, the exception is eaten silently. One of my strongest passions when coding is to know what is going on under the covers, and to be absolutely sure that error conditions are at the very least visible.

I'm starting to get the hang of events and delegates. It isn't yet another language for me, still a foreign language. But I figured there ought to be a way to write a delegate wrapper, so that I could take an event registration like this:

button1.Click += new EventHandler(button1_Click);

and using some yet-to-be-written class, make it look like this:

button1.Click += new WrappedHandler(new EventHandler(button1_Click));

where WrappedHandler would call the event handler passed into it, but inside a try-catch block, so that exceptions could be displayed.

I had to take a few stabs at it, and I ended up with three "new"s rather than the two I thought I would need, but here's something that works:

public class WrappedHandler
{
    private EventHandler handler;

    public WrappedHandler(EventHandler handler)
    {
        this.handler += handler;
    }

    public void Handler(object sender, EventArgs e)
    {
        try
        {
            handler(sender, e);
        }
        catch (Exception ex)
        {
            // Our handler threw an exception.
            // Show it.
            MessageBox.Show("Exception: " + ex.ToString());
            // Then re-throw it.
            throw;
        }
    }
}

Now the event handler can be registered like this:

button1.Click +=
    new EventHandler(
        new WrappedHandler(
            new EventHandler(
                button1_Click
            )
        ).Handler
    );

Is this the simplest it could be? Did I miss a left turn a half mile back?

BTW: as I was making a new tiny project to experiment with this, I noticed that all of the event handlers were nicely reporting exceptions. My real project still has exceptions which aren't being reported. I'll have to track down whether it's because they are different handlers, or because of the third-party controls we're using, or even because my predecessor is eating exceptions somewhere. Fun fun.

Comments

[gravatar]
Doug Holton 11:41 AM on 28 Mar 2005

You can also just handle the exception in your button click method, or install an app-wide exception handler: AppDomain.CurrentDomain.UnhandledException += your handler.

[gravatar]
Nils Jonsson 11:50 AM on 28 Mar 2005

I canít tell from what youíve mentioned whether this will turn up exceptions that are getting eaten, but you might also try listening to the Application.ThreadException event. It is designed to allow you to catch exceptions that your UI-thread event handlers may be throwing.

[gravatar]
Ned Batchelder 12:04 PM on 28 Mar 2005

So far, it looks like the problem is a overly-zealous third-party control. Fortunately, they provide a property to disable their eating of exceptions!

But these are great pointers guys, thanks!

[gravatar]
Julien Couvreur 12:17 PM on 28 Mar 2005

I may be missing something, but any reason why your WrappedHandler doesn't simply inherit EventHandler? This way it is an EventHandler (no need for wrapping back into an EventHandler), and you can implement the extra (exception catching) behavior.
Call "base" if you want to re-use some of the base class implementation by invoking one of its methods.

[gravatar]
Ned Batchelder 12:24 PM on 28 Mar 2005

I tried inheriting from EventHandler, it was my first approach. Maybe I did something wrong, but I was told that it was a sealed class and could not be inherited from. Can you send a sample that inherits from it?

[gravatar]
Julien Couvreur 1:27 PM on 28 Mar 2005

Ok, that's what I was missing :-P
You're right, EventHandler is sealed, which prevents inheriting it. That's a shame :-(

[gravatar]
Jeff Atwood 12:51 AM on 29 Mar 2005

There are circumstances where exceptions will be consumed "behind your back". Mostly having to do with threads and COM interop:

http://www.codinghorror.com/blog/archives/000216.html

However this can be subtle. For example, if your app supports drag and drop via the provided .NET mechanisms.. well, that's a COM operation behind the scenes-- so exceptions in the draggin' will be silently eaten. Really nasty.

Add a comment:

name
email
Ignore this:
not displayed and no spam.
Leave this empty:
www
not searched.
 
Name and either email or www are required.
Don't put anything here:
Leave this empty:
URLs auto-link and some tags are allowed: <a><b><i><p><br><pre>.