我有一个通用接口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 });
}
这种方法存在两个问题。首先,它使用缓慢的反射。其次,如果该方法抛出任何类型的异常,则将被包装在
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);
}
}
CreateUserCommandHandler
,但我不知道它是如何被实例化的。此外,你有Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
但这只会将其定义为ICommandHandler<CreateUserCommand>
,然后你对其运行Activator.CreateInstance
?你如何创建接口的实例? - Chris SinclairT
是您的ICommand
类型,为什么不将您的object handler
类型定义为ICommandHandler<T>
,它自然会有一个T Handle(T command)
方法? - Chris SinclairIoC.Get(handlerType)
зҡ„з»“жһңејәеҲ¶иҪ¬жҚў/зұ»еһӢеҢ–дёәICommandHandler<T>
пјҹиҝҷж ·еҒҡдҪҝе…¶д»–жүҖжңүдәӢжғ…йғҪеҸҳеҫ—еҫ®дёҚи¶ійҒ“пјҢжҳҜеҗ—пјҹзј–иҫ‘пјҡд№ҹи®ёжӮЁеҸҜд»ҘеҸ‘еёғICommandHandler<T>.Handle
ж–№жі•зҡ„зӯҫеҗҚпјҹ - Chris SinclairT
是一些基类或低级接口,来自调用代码,而command.GetType()
的实际结果是T
的高级子类吗?因为如果T
与command.GetType()
相同类型,那么这应该是微不足道的。 - Chris SinclairT
和command.GetType()
不同,否则它肯定是微不足道的。在运行时,T
是ICommand
,而command.GetType()
是CreateUserCommand
。 - Adam Rodger