【发布时间】:2013-04-10 15:53:14
【问题描述】:
我有一个通用接口 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对它?如何创建接口的实例? -
事实上,这是您使用的实际代码吗?如果
T是你的ICommand类型,那为什么不把你的object handler写成ICommandHandler<T>,其中自然会有T Handle(T command)方法呢? -
根据您的编辑,是否有什么阻止您将
IoC.Get(handlerType)的结果简单地转换/键入为ICommandHandler<T>?这样做会使其他一切变得微不足道,不是吗?编辑:也许您可以发布您的ICommandHandler<T>.Handle方法的签名? -
@ChrisSinclair 不幸的是
T与command.GetType()不同,否则它肯定是微不足道的。T在运行时是ICommand而command.GetType()是CreateUserCommand。
标签: c# generics reflection