C# 在泛型目标上动态调用方法

C# 在泛型目标上动态调用方法,c#,generics,reflection,C#,Generics,Reflection,我有一个通用接口ICommandHandler,该接口将有多个实现,每个实现用于处理ICommand的特定实现,例如: public class CreateUserCommand : ICommand { ... } public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... } 这种方法有两个问题。首先,它使用慢反射。其次,如果该方法抛出任何类型的异常,那么它将被包装在一个Ta

我有一个通用接口
ICommandHandler
,该接口将有多个实现,每个实现用于处理
ICommand
的特定实现,例如:

public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }
这种方法有两个问题。首先,它使用慢反射。其次,如果该方法抛出任何类型的异常,那么它将被包装在一个
TargetInvocationException
中,如果我重新抛出它,我将丢失堆栈跟踪

我通过创建一个委托并使用
DynamicInvoke
找到了一种调用方法,但这并不能解决异常问题(我不确定
DynamicInvoke
是否真的比
Invoke
好):

编辑2:如下所示,我无法将
IoC.Get(handlerType)
的结果强制转换为
ICommandHandler
,因为我在运行时得到了一个
InvalidCastException
。这是因为在运行时
T
实际上是
ICommand
,我认为这是因为命令类通过WCF到达,并且以某种方式丢失了它们的强类型。调用dispatcher的代码如下所示:

[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);
    }
}
试试这个

dynamic handler=Activator.CreateInstance(handlerType);
try
  {
         handler.Handle((dynamic)command);
   }

   catch
   {
   // do whatever you want 
   }
大多数DI容器(包括Ninject)允许您执行以下操作:

public void Dispatch<T>(T command) where T : ICommand
{
    ICommandHandler<T> handler = IoC.Get<ICommandHandler<T>>();
    handler.Handle(command);
}
但是,如果您发现将此代码添加到所有命令中令人不愉快,则可以使用反射

编辑这是一个基于反射的版本。您可以(也应该)缓存已编译的委托

interface ICommand { }
interface IHandle<TCommand> where TCommand : ICommand
{
    void Handle(TCommand command);
}

class CreateUserCommand : ICommand { }
class CreateUserHandler : IHandle<CreateUserCommand>
{
    public void Handle(CreateUserCommand command)
    {
        Console.Write("hello");
    }
}

[TestMethod]
public void build_expression()
{
    object command = new CreateUserCommand();
    object handler = new CreateUserHandler();

    Action<object, object> dispatcher = BuildDispatcher(command.GetType());
    dispatcher(handler, command);
}

private static Action<object, object> 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<Action<object, object>>(call, param1, param2);
    return lambda.Compile();
}
接口ICommand{}
接口i句柄,其中TCommand:ICommand
{
无效句柄(TCommand命令);
}
类CreateUserCommand:ICommand{}
类CreateUserHandler:IHandle
{
公共无效句柄(CreateUserCommand)
{
控制台。写(“你好”);
}
}
[测试方法]
public void build_表达式()
{
对象命令=新建CreateUserCommand();
对象处理程序=新建CreateUserHandler();
Action dispatcher=BuildDispatcher(command.GetType());
调度员(处理员、指挥);
}
专用静态操作BuildDispatcher(类型commandType)
{
var handlerType=typeof(IHandle)。MakeGenericType(commandType);
var handleMethod=handlerType.GetMethod(“句柄”);
var param1=表达式参数(typeof(object));
var param2=Expression.Parameter(typeof(object));
var handler=Expression.ConvertChecked(param1,handlerType);
var command=Expression.ConvertChecked(param2,commandType);
var call=Expression.call(处理程序、handleMethod、命令);
var lambda=Expression.lambda(调用,param1,param2);
返回lambda.Compile();
}

我不清楚预期目标是什么。你能为这个模式的使用添加一个代码示例吗?你声明了一个
CreateUserCommandHandler
,但我看不出它是如何被实例化的。此外,还有
typehandlerType=typeof(ICommandHandler)但这不就是将它定义为
ICommandHandler
,对其运行
Activator.CreateInstance
?如何创建接口的实例?实际上,这是您正在使用的实际代码吗?如果
T
是您的
ICommand
类型,为什么不将您的
对象处理程序
键入为
ICommandHandler
,其中自然会有一个
T Handle(T命令)
方法?根据您的编辑,是否有任何东西阻止您简单地投射/键入
IoC.Get(handlerType)的结果
作为
ICommandHandler
?这样做会使其他事情变得微不足道,不是吗?编辑:也许你可以发布你的
ICommandHandler.Handle
方法的签名?@ChrisSinclair不幸的是
T
command.GetType()
不同,否则它肯定是微不足道的
T
在运行时是
ICommand
,而
command.GetType()
CreateUserCommand
。我试图使用
dynamic
,但在运行时遇到一个异常,说我为
handler.Handle()
提供了一个无效的参数。我假设这是因为实现采用
ICommand
实现,而dispatcher采用接口类型。我实际上构建了您想要做的事情,这就是我所做的(当然,严格执行命令处理程序),我使用了编辑版本和表达式构建,它工作得非常完美。速度似乎与正确的方法调用(尤其是缓存结果)几乎相同,所有异常都正确抛出。
dynamic handler=Activator.CreateInstance(handlerType);
try
  {
         handler.Handle((dynamic)command);
   }

   catch
   {
   // do whatever you want 
   }
public void Dispatch<T>(T command) where T : ICommand
{
    ICommandHandler<T> handler = IoC.Get<ICommandHandler<T>>();
    handler.Handle(command);
}
class SomeCommand : ICommand
{
    // ...

    public void Dispatch(IoC ioc)
    {
        var handler = ioc.Get<IHandle<SomeCommand>>();
        handler.Handle(this);
    }
}
interface ICommand { }
interface IHandle<TCommand> where TCommand : ICommand
{
    void Handle(TCommand command);
}

class CreateUserCommand : ICommand { }
class CreateUserHandler : IHandle<CreateUserCommand>
{
    public void Handle(CreateUserCommand command)
    {
        Console.Write("hello");
    }
}

[TestMethod]
public void build_expression()
{
    object command = new CreateUserCommand();
    object handler = new CreateUserHandler();

    Action<object, object> dispatcher = BuildDispatcher(command.GetType());
    dispatcher(handler, command);
}

private static Action<object, object> 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<Action<object, object>>(call, param1, param2);
    return lambda.Compile();
}