MulticastDelegate和Exception处理:是否可以将它全部包装起来?

时间:2010-11-11 22:45:54

标签: c# .net

调用多播委托时,应使用GetInvocationList逐个调用委托:

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

有没有办法对此进行泛化,以便通过包装此迭代来回到单个调用以隐藏它,以便只能再次调用整个多播委托?这将更接近故意水平。

3 个答案:

答案 0 :(得分:6)

你可以这样做:

public static void CallAllAndCatch(this Action self)
{
    if (self == null)
        return;

    foreach (Action i in self.GetInvocationList()) {
        try { i(); }
        catch { }
    }
}

请注意,如果您发现自己使用泛型,可以使用泛型EventHandler<T>,但您不能为 任何类型的任何委托执行此操作,因为委托类型之间不能分配,即使它们是兼容的,并且在定义将特定泛型参数限制为具有特定签名的委托的泛型方法时,没有机制。 (我认为具有相同签名的委托被认为是以.NET 4开头的兼容类型。)

对于EventHandler<T>方法:

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 { }
    }
}

如果你不介意抛出性能和编译时类型检查管,你可以这样做,这将适用于任何委托类型:

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.
    }
}

答案 1 :(得分:2)

Hmya,这非常可疑。捕获异常并处理它只能在恢复程序的状态时才能正常工作,这样看起来好像从未发生过异常。对于MulticastDelegate而言完全是不可能的。

它的合同是你不知道哪种代码为它订阅了一个事件处理程序。如果那个完全未知的代码抛出异常并且没有自己处理,那么你根本就没有想到如何恢复状态。事件处理程序可能已经做了很多工作,变异状态,然后在接近结束时死亡。你没有希望不做“工作”部分,你不知道这些代码。它可能是在您编写代码后的几个月或几年内编写的。

真的不好主意,不要这样做。

答案 2 :(得分:1)

代理的类型是否已修复?您需要使用相当多的反射,或者您需要为每个可能的参数计数实现一个版本,就像有一个Action&lt; ...&gt;输入它。

应该与此类似(未经测试的记事本代码):

  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);
  }

您可能需要为单个演员代表添加特殊案例处理。