Dynamicznie wywołaj metodę na ogólnym celu
Mam ogólny interfejsICommandHandler<>
które będą miały wiele wdrożeń, każda do przetwarzania określonej implementacjiICommand
np .:
public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }
Kiedy otrzymamICommand
obiekt próbuję wysłać go dynamicznie do poprawnegoICommandHandler
. W tej chwili zastosowałem całkiem proste podejście do refleksji zInvoke
w mojej klasie dyspozytora:
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 });
}
Istnieją 2 problemy z tym podejściem. Po pierwsze wykorzystuje powolne odbicie. Po drugie, jeśli metoda rzuci jakikolwiek wyjątek, zostanie zapakowana wTargetInvocationException
i stracę ślad stosu, jeśli go wyrzucę.
Opracowałem sposób nawiązania połączenia, tworząc delegata i używającDynamicInvoke
ale to nie rozwiązuje problemu z wyjątkami (i nie jestem pewienDynamicInvoke
jest naprawdę lepszy niżInvoke
):
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);
}
Moje pytanie brzmi: czy jest lepszy sposób na osiągnięcie tego, co próbuję zrobić? Najlepiej byłoby, gdybyśmy zamiast wykonywać ankietę wykonali mocno wpisane połączenieobject
i patrząc w góręMethodInfo
. Zakładam, że nie jest to możliwe, ponieważ typ nie jest znany w czasie kompilacji.
Jeśli to nie jest możliwe, następnym skutecznym rozwiązaniem będzie wydajne rozwiązanie, które rzuci wyjątek bardziej natywnie.
Edytować: Zaktualizowano próbki kodu, aby wyjaśnić, że używam IoC (Ninject), aby utworzyćICommandHandler
w czasie wykonywania, nieActivator.CreateInstance()
jak po raz pierwszy umieściłem. Uwzględniono przykład, w jaki sposób zostanie on użyty zgodnie z żądaniem:
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'
Edytuj 2: Jak zasugerowano poniżej, nie mogę rzucić wynikuIoC.Get(handlerType)
doICommandHandler<T>
bo dostajęInvalidCastException
W czasie wykonywania. Dzieje się tak dlatego, że w czasie wykonywaniaT
jest aktualneICommand
Zakładam, że klasy poleceń docierają do WCF i jakoś udaje im się utracić mocne wpisywanie. Kod wywołujący dyspozytora wygląda tak:
[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);
}
}