MulticastDelegate and Exception handling: is it possible to wrap it all generically?

后端 未结 3 2102
逝去的感伤
逝去的感伤 2021-01-06 16:13

When calling a Multicast Delegate one should use GetInvocationList to call one by one the delegate:

public void IterateAll()
{
    if( _doExecute != null )
          


        
相关标签:
3条回答
  • 2021-01-06 16:45

    Is the type of the delegate fixed? You'll need to either use quite a bit of reflection, or you need to implement one version for each possible parametercount just like there is one Action<...> type for it.

    Should look similar to this(untested 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 case handling for single cast delegates.

    0 讨论(0)
  • 2021-01-06 16:49

    You could 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 yourself doing this a lot with e.g. EventHandler<T>, but you cannot do this generically for any delegate of any type since delegate types cannot be assigned between each other, even if they are compatible, and there is no mechanism when defining a generic method to restrict a specific generic parameter to be 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 type checking down the tube, you can do this, which will work with any delegate type:

    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.
        }
    }
    
    0 讨论(0)
  • 2021-01-06 16:55

    Hmya, this is very fishy. Catching an exception and handling it only really works well when you can restore the program's state so that it looks like the exception never happened. That is completely impossible to do for a MulticastDelegate.

    It's contract is that you have no idea what kind of code subscribed an event handler for it. If that completely unknown code throws an exception and didn't handle itself, you have no guess at all how to restore state. The event handler might have done a bunch of work, mutating state, then died somewhere near the end. You have no hope of un-doing the 'bunch of work' parts, that code is unknown to you. It might have been written months or years after you wrote your code.

    Really Bad Idea, don't do this.

    0 讨论(0)
提交回复
热议问题