Handling MulticastDelegate and Exception: Can I Wrap It All In General?

When calling a multicast delegate, you should use GetInvocationList to call the delegate one after the other:

public void IterateAll() { if( _doExecute != null ) { foreach( ExecuteCallback doSingleExecute in _doExecute.GetInvocationList() ) { try { doSingleExecute(); } catch { // This delegate threw an exception } } } } 

Is there a way to generalize this to return to a single call by wrapping this iteration to hide it, and so that the whole multicast delegate can be called again? It will be closer to the intentional level.

+2
source share
3 answers

You can do something like this:

 public static void CallAllAndCatch(this Action self) { if (self == null) return; foreach (Action i in self.GetInvocationList()) { try { i(); } catch { } } } 

Note that you can use generics if you find that you do this a lot with, for example, EventHandler<T> , but you cannot generally do this for any delegate of any type, because delegate types cannot be assigned between by themselves, even if they are compatible, and there is no mechanism in determining a common method for restricting a specific general parameter to a delegate with a specific signature. (I think delegates with identical signatures are considered compatible types starting with .NET 4.)

For the EventHandler<T> approach:

 public static void CallAllAndCatch(this EventHandler<T> self, object sender, T args) where T : EventArgs { if (self == null) return; foreach (EventHandler<T> i in self.GetInvocationList()) { try { i(sender, args); } catch { } } } 

If you don't mind throwing performance and compile time while checking the handset, you can do this, which will work with any type of delegation:

 public static void CallAllAndCatch(this Delegate self, params object[] args) where T : EventArgs { if (self == null) return; foreach (Delegate i in self.GetInvocationList()) { try { i.DynamicInvoke(args); } catch (MemberAccessException) { throw; } // A type of something in args isn't compatible with the delegate signature. catch (TargetException) { throw; } // The delegate itself is invalid. catch { } // Catch everything else. } } 
+5
source

Hm, this is very suspicious. Capturing an exception and handling it only really works well when you can restore the state of the program so that it looks like the exception never occurred. This is not possible for MulticastDelegate.

The contract is that you have no idea what code the event handler signed for it. If this completely unknown code throws an exception and does not handle itself, you don’t know how to restore the state at all. The event handler may have done a ton of work, changed state, and then died somewhere near the end. You have no hope that you will not perform parts of the "bunch of work", this code is unknown to you. Perhaps it was written months or years after you wrote your code.

Really, a bad idea, do not do this.

+2
source

Is the delegate type fixed? You will need to either reflect a little, or you need to implement one version for each possible parameter, as if it had one type of Action <....

It should look similar to this (unverified notepad code):

  public static Action WrapAction(Action a) { var invList = ((MultiCastDelegate)a).GetInvocationList(); for (int i = 0; i < invList.Length; i++) { invList[i] = ()=>{try invList[i] catch {...} }); } return (Action)MulticastDelegate.Combine(invList); } 

And you probably need to add special handling for individual delegates.

+1
source

All Articles