У меня есть универсальный интерфейс ICommandHandler<>
, который будет иметь несколько реализаций каждого для обработки конкретной реализации ICommand
, например:Динамически вызова метода общей цели
public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }
Когда я Дано ICommand
объект Я пытаюсь отправить его динамически на правильный ICommandHandler
. На данный момент я использовал довольно простой подход отражения с 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");
method.Invoke(handler, new object[] { command });
}
Есть 2 проблемы с этим подходом. Во-первых, он использует медленное отражение. Во-вторых, если метод выбрасывает какое-либо исключение, он будет завернут в TargetInvocationException
, и я потеряю трассировку стека, если я его повторно выброшу.
Я разработал способ, чтобы сделать вызов путем создания делегата и использование DynamicInvoke
, но это не решает проблему с исключениями (и я не уверен, что DynamicInvoke
действительно лучше, чем 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);
}
Мой вопрос в том, есть ли лучший способ достичь того, что я пытаюсь сделать? Предпочтительно я мог бы сделать строго типизированный вызов вместо получения object
и поискать MethodInfo
. Я предполагаю, что это невозможно, потому что тип не знает во время компиляции.
Если это невозможно, то лучшим вариантом будет эффективное решение, которое сделает исключение более «естественным».
Edit: обновленные образцы кода для уточнения того, что я использую IoC (Ninject) для создания ICommandHandler
во время выполнения, а не Activator.CreateInstance()
как я первый положил. Включенный пример того, как это будет использоваться в соответствии с просьбой:
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'
Edit 2: Как предлагается ниже, я не могу бросить результат IoC.Get(handlerType)
к ICommandHandler<T>
, потому что я получаю InvalidCastException
во время выполнения. Это связано с тем, что во время выполнения T
на самом деле ICommand
, я предполагаю, что классы команд прибывают через WCF и каким-то образом теряют свою сильную типизацию. Код, который вызывает диспетчеру выглядит что-то вроде:
[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);
}
}
Я смущен тем, что это за цель. Можете ли вы добавить пример кода для использования этого шаблона? – Bobson
Вы объявляете 'CreateUserCommandHandler', но я не вижу, как он создается. Кроме того, у вас есть 'Type handlerType = typeof (ICommandHandler <>). MakeGenericType (commandType);' но не будет ли это просто определять его как «ICommandHandler», с которым вы запускаете «Activator.CreateInstance»? Как создать экземпляр интерфейса? –
Фактически, это код _actual_, который вы используете? Если 'T' - ваш тип' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '? –