动态调用通用目标上的方法

我有一个通用接口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)的结果InvalidCastExceptionICommandHandler因为我在运行时获得了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 dispatcher = BuildDispatcher(command.GetType()); dispatcher(handler, command); } private static Action BuildDispatcher(Type commandType) { var handlerType = typeof(IHandle<>).MakeGenericType(commandType); var handleMethod = handlerType.GetMethod("Handle"); var param1 = Expression.Parameter(typeof(object)); var param2 = Expression.Parameter(typeof(object)); var handler = Expression.ConvertChecked(param1, handlerType); var command = Expression.ConvertChecked(param2, commandType); var call = Expression.Call(handler, handleMethod, command); var lambda = Expression.Lambda>(call, param1, param2); return lambda.Compile(); } 

试试这个

 dynamic handler=Activator.CreateInstance(handlerType); try { handler.Handle((dynamic)command); } catch { // do whatever you want }