保留动态调用方法的exception

  • 有关
  • 有关

我想动态调用一个MethodInfo对象,并从内部抛出任何exception向外传递,就像它被正常调用一样。

我看来有两种选择。 它们概述如下。

选项1维护MyStaticFunction抛出的exception类型,但StackTracethrow被破坏。

选项2维护exception的StackTrace ,但exception的类型始终是TargetInvocationException 。 我可以提取InnerException及其类型,但这意味着我不能写这个例子:

 try { DoDynamicCall(); } catch (MySpecialException e) { /* special handling */ } 

选项1:

 void DoDynamicCall() { MethodInfo method = /*referencing MyClass method void MyStaticFunction(int x)*/; try { method.Invoke(null, new object[] { 5 }); } catch (TargetInvocationException e) { throw e.InnerException; } } 

选项2:

 void DoDynamicCall() { MethodInfo method = /*referencing MyClass method void MyStaticFunction(int x)*/; method.Invoke(null, new object[] { 5 }); } 

我真正想要的是让DoDynamicCall调用者接收exception,好像他们已经调用了这个:

 void DoDynamicCall() { MyClass.MyStaticFunction(5); } 

有没有办法获得选项1选项2的好处?

编辑:

我希望我拥有选项 (当场发明了特殊的新C#关键字rethrow ):

 void DoDynamicCall() { MethodInfo method = /*referencing MyClass method void MyStaticFunction(int x)*/; try { method.Invoke(null, new object[] { 5 }); } catch (TargetInvocationException e) { //Magic "rethrow" keyword passes this exception //onward unchanged, rather than "throw" which //modifies the StackTrace, among other things rethrow e.InnerException; } } 

这也可以消除对这个怪人的需要,因为你可以使用rethrow e; 代替:

 try { ... } catch (Exception e) { if (...) throw; } 

一般来说,这将是一种解耦的方法throw; 从要求“我必须直接在一个阻止块。”

这是我提出的解决方案。 它完成了工作。 我仍然对其他答案感兴趣,因为可能会有更简单或更清洁的东西。

  • 当你想要throw;的function时throw; 但是你要传递的exception不是当前catch块的例外,使用throw Functional.Rethrow(e);
  • Functional.TryCatch替换try...catch...
  • Functional.TryCatchFinally替换try...catch...finally...

这是代码:

 //Need a dummy type that is throwable and can hold an Exception public sealed class RethrowException : Exception { public RethrowException(Exception inner) : base(null, inner) { } } public static Functional { public static Exception Rethrow(Exception e) { return new RethrowException(e); } public static void TryCatch(Action _try, Action _catch) { try { _try(); } catch (RethrowException e) { _catch(e.InnerException); } catch (Exception e) { _catch(e); } } public static T TryCatch(Func _try, Func _catch) { try { return _try(); } catch (RethrowException e) { return _catch(e.InnerException); } catch (Exception e) { return _catch(e); } } public static void TryCatchFinally( Action _try, Action _catch, Action _finally) { try { _try(); } catch (RethrowException e) { _catch(e.InnerException); } catch (Exception e) { _catch(e); } finally { _finally(); } } public static T TryCatchFinally( Func _try, Func _catch, Action _finally) { try { return _try(); } catch (RethrowException e) { return _catch(e.InnerException); } catch (Exception e) { return _catch(e); } finally { _finally(); } } } 

更新

在.NET 4.5中,有新的System.Runtime.ExceptionServices.ExceptionDispatchInfo类 。 这可以用来捕获exception:

 var capturedException = ExceptionDispatchInfo.Capture(e); 

然后这用于恢复抛出exception:

 capturedException.Throw(); 

不,我不相信有办法让两者都有好处。 但是,抛出e.InnerException仍然允许您获取原始堆栈跟踪,因为您只需使用e.InnerException.StackTrace来获取原始堆栈跟踪。 因此,简而言之,您应该使用选项1

最好的选择是选项3 :根本不使用reflection,而是使用Expression.Compile()

而不是这样做:

 static void CallMethodWithReflection(MethodInfo method) { try { method.Invoke(null, new object[0]); } catch (TargetInvocationException exp) { throw exp.InnerException; } } 

试着瞄准这个:

 private static void CallMethodWithExpressionCompile(MethodInfo method) { Expression.Lambda(Expression.Call(method)).Compile()(); } 

需要注意的是,您需要知道方法签名,尽管您可以编写动态构建表达式的代码以适应多个签名之一。

您可能无法始终使用此技术,但是当您这样做时,它是最佳选择。 对于所有意图和目的,它就像呼叫任何其他代表。 如果你进行多次调用,它也比reflection更快(在这种情况下只编译一次,并在编译的委托上保持句柄)。

我有一个类似的问题,并想出了这个:

 ///  /// Attempts to throw the inner exception of the TargetInvocationException ///  ///  [DebuggerHidden] private static void ThrowInnerException(TargetInvocationException ex) { if (ex.InnerException == null) { throw new NullReferenceException("TargetInvocationException did not contain an InnerException", ex); } Exception exception = null; try { //Assume typed Exception has "new (String message, Exception innerException)" signature exception = (Exception) Activator.CreateInstance(ex.InnerException.GetType(), ex.InnerException.Message, ex.InnerException); } catch { //Constructor doesn't have the right constructor, eat the error and throw the inner exception below } if (exception == null || exception.InnerException == null || ex.InnerException.Message != exception.Message) { // Wasn't able to correctly create the new Exception. Fall back to just throwing the inner exception throw ex.InnerException; } throw exception; } 

它的使用示例如下:

 try { return typeof(MyType).GetMethod(methodName, BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(new[] { myType) }) .Invoke(null, parameters); } catch (TargetInvocationException ex) { ThrowInnerException(ex); throw new Exception("Throw InnerException didn't throw exception"); }