Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoke a method through reflection without getting TargetInvocationException

Tags:

c#

reflection

I've found a method using reflection (and got it's MethodInfo). How can I invoke it without getting TargetInvocationException when exceptions are thrown?

Update

I'm creating a command implementation where the commands are handled by classes which implemement

public interface ICommandHandler<T> where T : class, ICommand
{
    public void Invoke(T command);
}

Since there is one dispatcher which takes care of find and map all handlers to the correct command I can't invoke the methods directly but by using reflection. Something like:

var handlerType = tyepof(IHandlerOf<>).MakeGenericType(command.GetType());
var method = handlerType.GetMethod("Invoke", new [] { command.GetType() });
method.Invoke(theHandler, new object[]{command});

It works fine, but I want all exceptions to get passed on to the code that invoked the command.

So that the caller can use:

try
{
    _dispatcher.Invoke(new CreateUser("Jonas", "Gauffin"));
}
catch (SomeSpecificException err)
{
    //handle it.
}

Instead of having to catch TargetInvocationException.

(I know that I can throw the inner exception, but that's pretty worthless since the stack trace is destroyed)

Update2

Here is a possible solution..

But it seems more like a hack. Aren't there a better solution? Maybe with expressions or something?

like image 663
jgauffin Avatar asked Jun 21 '12 14:06

jgauffin


2 Answers

Create a Delegate from the MethodInfo (through one of the overloads of Delegate.CreateDelegate) and invoke that instead. This won't wrap any exception thrown by the method inside a TargetInvocationException like MethodInfo.Invoke does.

class Foo
{
    static void ThrowingMethod()
    {
        throw new NotImplementedException();
    }

    static MethodInfo GetMethodInfo()
    {
        return typeof(Foo)
                .GetMethod("ThrowingMethod", BindingFlags.NonPublic | BindingFlags.Static);
    }

    // Will throw a NotImplementedException
    static void DelegateWay()
    {
        Action action = (Action)Delegate.CreateDelegate
                                    (typeof(Action), GetMethodInfo());
        action();
    }

    // Will throw a TargetInvocationException 
    // wrapping a NotImplementedException
    static void MethodInfoWay()
    {
        GetMethodInfo().Invoke(null, null);
    }
}

EDIT:

(As the OP has pointed out, DynamicInvoke won't work here since it wraps too)

Based on your update, I would just use dynamic:

((dynamic)theHandler).Invoke(command);
like image 104
Ani Avatar answered Nov 14 '22 04:11

Ani


You can't. That's the specified way that exceptions are propagated by invoking a method via reflection. You can always catch TargetInvocationException and then throw the "inner" exception obtained via the InnerException property, if you want the effect to be the original exception being thrown.

(You'll lose the original stack trace, mind you. It's possible that there's a way to prevent that, but it's tricky. I believe there may be more support for this in .NET 4.5; I'm not sure.)

like image 39
Jon Skeet Avatar answered Nov 14 '22 03:11

Jon Skeet