在具有可变数量参数的动态调用中控制异常处理
本文关键字:动态 调用 异常处理 控制 参数 | 更新日期: 2023-09-27 18:28:07
在昨天解决的一个线程中,@hvd向我展示了如何通过.Invoke对异常处理进行"控制"。在处理未知类型的委托时(在Isis2等库中可以看到这个问题,最终用户提供多态事件处理程序,库类型匹配以决定调用哪个)。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);
我的问题是:有人能提出一种方法来做同样的事情,适用于任意数量的论点吗?很明显,我可以为1 arg、2等的情况编写切换语句和代码。但是,在mi.GetParameters().Length告诉我们有多少个参数的情况下,有没有方法做到这一点?
对于那些不想点击链接的人来说,作为一个胶囊摘要,核心问题是:在进行这些类型的动态上调用时,最终用户(注册了被调用的方法)可能会由于错误而引发异常。事实证明,当不在Visual Studio下运行时——当直接在CLR中运行时——C#.Invoke将捕获并重新抛出异常,并将它们打包为InvocationTargetException内的内部异常。这会展开堆栈,并导致用户认为bug是调用.Invoke的代码(例如MY代码)的某种问题。这就是为什么C#参考手册认为catch/rethrow是一种糟糕的编码实践:应该只捕获计划处理的异常。。。
hvd解释说,这基本上是因为Invoke对参数的数量或类型一无所知,而且在这种模式下,显然出于某种原因,会捕获并重新抛出异常。他的变通方法基本上确定了参数的数量(示例中的泛型:Action<,>),这显然足够了。Invoke不会执行"通用捕获"。但是,为了将他的示例用于任意代码,我需要每个可能数量的参数都有一个case。可以(毕竟,谁会想要超过16个?)但丑陋!
因此,今天的挑战是:改进代码,以便使用类似的C#的3行代码片段,无论有多少参数,它都能工作。当然,生成的委托也需要是可调用的,大概有一个对象向量,每个参数一个。。。
PS:悲观主义的一个原因是:行动本身有16种形式,有1到16个论点。因此,对我来说,这表明C#开发人员没有看到更通用的方法来实现这一点,最终使用switch语句得到了与我相对应的版本(我猜switch会有0到16个参数的情况,因为我需要一个带有N个类型参数的Action<…>来处理N个用户提供的参数!)
我不想永远打开它,所以我已经尽我所能来理解核心问题,包括下载Mono中.Invoke的代码。据我所知,最初的问题只是由于一种优化,这种优化有利于更快的调用,而代价是在对具有参数向量的对象执行动态Invoke时捕获异常。使用通用模板创建的动态委托的代码中根本没有这个陷阱
这不是一个很好的答案,但如果不能访问Invoke的.NET实现,显然不可能给出更好的答案。