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 eigentlichICommand
Ich 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);
}
}