我有一个通用接口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 });
}
这种方法有两个问题。首先它使用慢反射。其次,如果该方法抛出任何类型的异常,那么它将被包裹在 a 中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
. 我认为这是不可能的,因为在编译时不知道类型。
如果这是不可能的,那么一个更“本机”抛出异常的有效解决方案将是下一个最佳选择。
编辑:更新代码示例以阐明我使用 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'
编辑 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);
}
}