Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I stop exceptions trashing my delegate chain?

I have come across what must be a common problem. When I have an event which may be subscribed to by several different classes, an exception thrown by one of these classes will kill the callback chain; as I do not know a priori in what order the callback is carried out, this can result in unpredictable state changes for some classes and not for others.

In the bible (CLR via C#, I am using C# 2.0) there is a short paragraph about using MulticastDelegate.GetInvocationList to get around this, but nothing more. So my question is: what is the best way to deal with this? Do I have to use MulticastDelegate.GetInvocationList every time I have an event?? Or do I need to enclose all methods which may be called as part of the delegate chain in some kind of rollback mechanism? Why are all these options so complicated in comparison to the simple event/delegate model which is so easy to use in C#? And how can I use the simple way without ending up with corrupted state?

Thanks!

like image 944
Joel in Gö Avatar asked Jan 12 '09 09:01

Joel in Gö


1 Answers

If you simply invoke a delegate, it will call all the target methods in order. You need to use GetInvocationList if you want to execute them individually - for example:

  • to check Cancel after each
  • to capture the return value of each
  • to continue after failure of an individual target

As for the best way to use it: how do you want it to behave? It isn't clear to me... for example, this might suit an extension method quite well:

static void InvokeIgnoreErrors(this EventHandler handler,
        object sender) {
    if(handler != null) {
        foreach(EventHandler subHandler in handler.GetInvocationList()) {
            subHandler(sender, EventArgs.Empty);
        }
    }
}

Then you can just call myHandler.InvokeIgnoreErrors(this); (for example).

Another example might be:

static bool InvokeCheckCancel(this CancelEventHandler handler,
        object sender) {
    if(handler != null) {
        CancelEventArgs args = new CancelEventArgs(false);
        foreach(CancelEventHandler subHandler in handler.GetInvocationList()) {
            subHandler(sender, args);
            if(args.Cancel) return true;
        }
    }
    return false;
}

which stops after the first event requests cancellation.

like image 102
Marc Gravell Avatar answered Sep 29 '22 10:09

Marc Gravell