保留动态调用方法的exception
- 有关
- 有关
我想动态调用一个MethodInfo
对象,并从内部抛出任何exception向外传递,就像它被正常调用一样。
我看来有两种选择。 它们概述如下。
选项1维护MyStaticFunction
抛出的exception类型,但StackTrace
因throw
被破坏。
选项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
。
而不是这样做:
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"); }