Rufen Sie dynamisch eine Methode für ein generisches Ziel auf

Ich habe eine generische SchnittstelleICommandHandler<> Das wird eine Reihe von Implementierungen haben, von denen jede für die Verarbeitung einer bestimmten Implementierung vonICommand, z.B.:

public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }

Wenn mir eine gegeben wirdICommand Objekt Ich versuche es dynamisch an die richtige zu versendenICommandHandler. Im Moment habe ich einen ziemlich einfachen Reflexionsansatz mit einerInvoke In meiner Dispatcher-Klasse:

public void Dispatch<T>(T command) where T : ICommand
{
    Type commandType = command.GetType();
    Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
    object handler = IoC.Get(handlerType);
    MethodInfo method = handlerType.GetMethod("Handle");

    method.Invoke(handler, new object[] { command });
}

Bei diesem Ansatz gibt es zwei Probleme. Erstens wird langsame Reflexion verwendet. Zweitens, wenn die Methode irgendeine Art von Ausnahme auslöst, wird sie in a eingeschlossenTargetInvocationException und ich werde die Stapelspur verlieren, wenn ich es erneut werfe.

Ich habe eine Möglichkeit gefunden, den Anruf zu tätigen, indem ich einen Delegaten erstellt und benutzeDynamicInvoke aber das löst das Problem mit Ausnahmen nicht (und ich bin nicht sicherDynamicInvoke ist wirklich nicht besser alsInvoke):

public void Dispatch<T>(T command) where T : ICommand
{
    Type commandType = command.GetType();
    Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
    object handler = IoC.Get(handlerType);
    MethodInfo method = handlerType.GetMethod("Handle");

    Type actionType = typeof(Action<>).MakeGenericType(commandType);
    Delegate action = Delegate.CreateDelegate(actionType, handler, method);
    action.DynamicInvoke(command);
}

Meine Frage ist, gibt es einen besseren Weg, um das zu erreichen, was ich versuche? Am liebsten könnte ich einen stark getippten Anruf tätigen, anstatt eine zu erhaltenobject und das nachschlagenMethodInfo. Ich gehe jedoch davon aus, dass dies nicht möglich ist, da der Typ zur Kompilierungszeit nicht bekannt ist.

Wenn dies nicht möglich ist, ist eine effiziente Lösung, die die Ausnahme eher "nativ" auslöst, die nächstbeste Option.

Bearbeiten: Die Codebeispiele wurden aktualisiert, um zu verdeutlichen, dass ich IoC (Ninject) verwende, um das zu erstellenICommandHandler zur Laufzeit nichtActivator.CreateInstance() wie ich zuerst sagte. Anbei ein Beispiel, wie dies wie gewünscht verwendet werden würde:

var command = new CreateUserCommand() { Name = "Adam Rodger" };
var dispatcher = new CommandDispatcher();
dispatcher.Dispatch(command);
// this would send the message to CreateUserCommandHandler.Handle(command) 
// dynamically and any exceptions would come back 'natively'

Bearbeiten 2: Wie unten vorgeschlagen, kann ich das Ergebnis von nicht besetzenIoC.Get(handlerType) zuICommandHandler<T> weil ich eine kriegeInvalidCastException zur Laufzeit. Dies liegt daran, zur LaufzeitT ist eigentlichICommandIch gehe davon aus, dass die Befehlsklassen über WCF ankommen und irgendwie ihre starke Typisierung verlieren. Der Code, der den Dispatcher aufruft, sieht ungefähr so ​​aus:

[ServiceContract]
public class CommandService
{
    [OperationContract]
    public void Execute(ICommand command) // no type information
    {
        var dispatcher = new CommandDispatcher(); // injected by IoC in real version
        dispatcher.Dispatch(command);
    }
}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage