In a 昨天的话题已解决 https://stackoverflow.com/questions/9726032/puzzle-involving-unwound-stacks-on-dynamic-invoke/9750323,@hvd 向我展示了如何在处理未知类型的委托时通过 .Invoke 来“控制”异常处理(在像这样的库中出现的问题)Isis2 http://isis2.codeplex.com,其中最终用户提供多态事件处理程序和库类型匹配来决定调用哪个)。 Hvd 的建议围绕着了解上行处理程序接收到多少个参数,然后使用该信息来构造正确类型的泛型,这使他能够构造动态对象并调用它。该序列产生了对异常处理的完全控制。
他建议的核心是 Isis2 可以考虑以这种方式进行上行调用:
MethodInfo mi = typeof(Program).GetMethod("Foo", BindingFlags.Static | BindingFlags.NonPublic);
Delegate del = Delegate.CreateDelegate(typeof(Action<,>).MakeGenericType(mi.GetParameters().Select(p => p.ParameterType).ToArray()), mi);
((dynamic)del).Invoke(arg0, arg1);
这是我的问题:任何人都可以建议一种方法来做同样的事情,适用于任意数量的参数吗?显然我可以做一个 switch 语句并为 1 arg、2 等的情况编写代码。但是有没有办法在 mi.GetParameters().Length 告诉我们有多少个参数的情况下做到这一点?
对于那些不想单击链接的人来说,核心问题是这样的:在进行此类动态向上调用时,最终用户(注册了被调用的方法)可能会因错误而抛出异常。事实证明,当不在 Visual Studio 下运行时(当直接在 CLR 中运行时),C# .Invoke 将捕获并重新抛出异常,将它们作为内部异常打包在 InitationTargetException 中。这会展开堆栈并导致用户将错误视为调用 .Invoke 的代码(例如,我的代码)存在某种问题。这就是为什么 C# 参考手册认为 catch/rethrow 是一种糟糕的编码实践:应该只捕获计划处理的异常......
hvd 解释说,这基本上是因为 .Invoke 不知道参数的数量或类型,并且在该模式下,显然会出于某种原因捕获并重新抛出异常。他的解决方法本质上是确定参数的数量(示例中的通用参数:Action),这显然足以使 .Invoke 不会执行“通用捕获”。但要将他的示例用于任意代码,我需要为每个可能的参数数量提供一个案例。可行(毕竟,谁会想要超过 16 个?),但丑陋!
因此,今天的挑战是:改进该代码,以便使用类似的 3 行 C# 代码片段,无论有多少参数都可以正常工作。当然,生成的委托也需要可调用,大概是一个对象向量,每个参数一个......
PS:悲观的原因之一:行动本身有 16 种形式,有 1 到 16 个论点。所以对我来说,这表明 C# 开发人员没有看到更通用的方法,最终使用 switch 语句得到了与我相对应的版本(我猜 switch 会有 0 到 16 个参数的情况) ,因为我需要一个带有 N 类型参数的 Action<...> 来处理 N 个用户提供的参数!)