从调用Invoke(反射)中获取正确的异常类型。

10 浏览
0 Comments

从调用Invoke(反射)中获取正确的异常类型。

在我们的集成项目中,我们使用反射动态委托给一系列特定的处理器对象。

var genericType = typeof(IMessageProcessor<>).MakeGenericType(parseResponse.DomainMessage.GetType());
var miProcessMessage = genericType.GetMethod("ProcessMessage");
miProcessMessage.Invoke(processor, new object[] { parseResponse.DomainMessage });

现在,在某些情况下,其中一个处理器需要抛出一个特定的异常,例如当他在预期位置找不到文件时。

throw new ResourceNotFoundException(string.Format("无法找到文件 {0}", filePath));

我们的处理器想要检查此异常,以确定是否需要重试或中止处理(基于一个与此无关的基于时间的机制)。

catch(ResourceNotFoundException ex)
{
   // 做一些操作
}

问题在于,调用似乎"吞噬"了特定的ResourceNotFoundException,并重新抛出一个说"通过调用的目标引发了异常"的System.Exception。

是否有办法通过反射保留ResourceNotFoundException实际上被抛出而不是Exception的事实?

0
0 Comments

问题的原因是在使用反射调用MethodBase.Invoke(Object, Object[])方法时,如果被调用方法抛出异常,那么该异常会被包装在一个TargetInvocationException中。为了获取到原始的异常类型,需要访问内部异常并进行处理或将其重新抛出以在堆栈中继续处理。

解决方法是使用try-catch块来捕获可能抛出的TargetInvocationException,然后通过访问内部异常InnerException来获取原始的异常类型。下面是一个示例代码:

var genericType = typeof(IMessageProcessor<>).MakeGenericType(parseResponse.DomainMessage.GetType());
var miProcessMessage = genericType.GetMethod("ProcessMessage");
try {
    miProcessMessage.Invoke(processor, new object[] { parseResponse.DomainMessage });
catch (TargetInvocationException ex)
{
    throw ex.InnerException;
}

需要注意的是,这种方法会替换内部异常的堆栈跟踪信息。如果想要保留原始异常的堆栈跟踪信息,可以参考Stack Overflow上这个答案中描述的(在技术上不支持的)方法。

另外需要注意的是,对于内置的异常类型,如SqlExceptionTransactionException,它们并不会被包装在TargetInvocationException中,因此可以直接在它们自己的异常块中处理。而对于其他异常类型,需要查看内部异常的类型来进行处理。

0