动态调用通用目标上的方法
我有一个通用接口ICommandHandler
,它将具有许多实现,每个实现用于处理ICommand
的特定实现,例如:
public class CreateUserCommand : ICommand { ... } public class CreateUserCommandHandler : ICommandHandler { ... }
当我给出一个ICommand
对象时,我试图将它动态地分配给正确的ICommandHandler
。 目前我在调度程序类中使用了一个非常直接的reflection方法和一个Invoke
:
public void Dispatch(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 }); }
这种方法存在两个问题。 首先它使用慢reflection。 其次,如果该方法抛出任何类型的exception,那么它将被包装在TargetInvocationException
,如果我重新抛出它,我将丢失堆栈跟踪。
我通过创建委托和使用DynamicInvoke
来制定调用的方法,但这并没有解决exception的问题(我不确定DynamicInvoke
是否真的比Invoke
更好):
public void Dispatch(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
。 我认为这是不可能的,因为在编译时不知道类型。
如果那是不可能的,那么一个有效的解决方案会更加“原生”地抛出exception将成为下一个最佳选择。
编辑 :更新了代码示例以澄清我正在使用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)
的结果InvalidCastException
为ICommandHandler
因为我在运行时获得了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); } }
大多数DI容器(包括Ninject)允许您执行以下操作:
public void Dispatch(T command) where T : ICommand { ICommandHandler handler = IoC.Get>(); handler.Handle(command); }
如果你不知道命令的类型(换句话说,如果是typeof(T) != command.GetType()
),使用double-dispatch是最简单的方法:
class SomeCommand : ICommand { // ... public void Dispatch(IoC ioc) { var handler = ioc.Get>(); handler.Handle(this); } }
但如果您发现将此代码添加到所有令人反感的命令中,您可以使用reflection。
编辑这是一个基于reflection的版本。 您可以(并且应该)缓存已编译的委托。
interface ICommand { } interface IHandle where TCommand : ICommand { void Handle(TCommand command); } class CreateUserCommand : ICommand { } class CreateUserHandler : IHandle { public void Handle(CreateUserCommand command) { Console.Write("hello"); } } [TestMethod] public void build_expression() { object command = new CreateUserCommand(); object handler = new CreateUserHandler(); Action
试试这个
dynamic handler=Activator.CreateInstance(handlerType); try { handler.Handle((dynamic)command); } catch { // do whatever you want }