Wrapping delegates in C#

Monday 28 March 2005This is over 19 years old. Be careful.

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]
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]
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]
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]
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]
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]
Ok, that's what I was missing :-P
You're right, EventHandler is sealed, which prevents inheriting it. That's a shame :-(
[gravatar]
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.
[gravatar]

I ended up going this route, adding an ErrorWrapper method on my base class which wraps the handler in a lamba: (This is a .NET MAUI project)

public EventHandler ErrorWrapper(EventHandler handler)
{
    return new EventHandler(async (object? sender, EventArgs e) =>
    {
        try
        {
            handler(sender, e);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);

            await this.ShowErrorPopup(
                "Unexpected Error",
                ex.Message
            );
        }
    });
}

And an async version for any handlers that have awaits inside them:

public delegate Task AsyncEventHandler(object? sender, EventArgs e);

public EventHandler ErrorWrapper(AsyncEventHandler handler)
{
    return new EventHandler(async (object? sender, EventArgs e) =>
    {
        try
        {
            await handler(sender, e);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);

            await this.ShowErrorPopup(
                "Unexpected Error",
                ex.Message
            );
        }
    });
}

Usage:

// Attach event handlers wrapped in error handler logic.
this.btnDelete.Clicked += this.ErrorWrapper(this.btnDelete_Clicked);

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.