I want to dynamically invoke a MethodInfo
object and have any exceptions that get thrown from inside of it pass outward as if it were called normally.
I have two options it seems. They're outlined below.
Option 1 maintains the type of the exception thrown by MyStaticFunction
, but the StackTrace
is ruined because of the throw
.
Option 2 maintains the StackTrace
of the exception, but the type of the exception is always TargetInvocationException
. I can pull out the InnerException
and its type, but that means that I can't write this for example:
try { DoDynamicCall(); }
catch (MySpecialException e) { /* special handling */ }
Option 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;
}
}
Option 2:
void DoDynamicCall()
{
MethodInfo method = /*referencing MyClass method void MyStaticFunction(int x)*/;
method.Invoke(null, new object[] { 5 });
}
What I really want is for callers to DoDynamicCall
to receive exceptions as if they had called this:
void DoDynamicCall()
{
MyClass.MyStaticFunction(5);
}
Is there a way to get the benefits of both Option 1 and Option 2?
The option I wish I had (invented special new C# keyword rethrow
on the spot):
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;
}
}
This would also eliminate the need for this weirdo, because you could use rethrow e;
instead:
try { ... }
catch (Exception e)
{
if (...)
throw;
}
In general, it would be a way to decouple throw;
from the requirement "I have to be directly in a catch block."
Here's the solution I came up with. It gets the job done. I'm still interested in other answers as there might be something easier or cleaner.
throw;
but the exception you want to pass on is not the exception of the current catch
block, use throw Functional.Rethrow(e);
try...catch...
with Functional.TryCatch
try...catch...finally...
with Functional.TryCatchFinally
Here's the code:
//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<Exception> _catch)
{
try { _try(); }
catch (RethrowException e) { _catch(e.InnerException); }
catch (Exception e) { _catch(e); }
}
public static T TryCatch<T>(Func<T> _try, Func<Exception, T> _catch)
{
try { return _try(); }
catch (RethrowException e) { return _catch(e.InnerException); }
catch (Exception e) { return _catch(e); }
}
public static void TryCatchFinally(
Action _try, Action<Exception> _catch, Action _finally)
{
try { _try(); }
catch (RethrowException e) { _catch(e.InnerException); }
catch (Exception e) { _catch(e); }
finally { _finally(); }
}
public static T TryCatchFinally<T>(
Func<T> _try, Func<Exception, T> _catch, Action _finally)
{
try { return _try(); }
catch (RethrowException e) { return _catch(e.InnerException); }
catch (Exception e) { return _catch(e); }
finally { _finally(); }
}
}
In .NET 4.5 there is the new System.Runtime.ExceptionServices.ExceptionDispatchInfo
class. This can be used to capture an exception:
var capturedException = ExceptionDispatchInfo.Capture(e);
And then later this is used to resume throwing the exception:
capturedException.Throw();
No, I don't believe there is a way to have the benefits of both. However, throwing e.InnerException
will still allow you to get the original stacktrace, because you can simply use e.InnerException.StackTrace
to get the original stack trace. So, in short, you should use option 1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With