gpt4 book ai didi

c# - 在通用目标上动态调用方法

转载 作者:太空狗 更新时间:2023-10-30 00:42:22 25 4
gpt4 key购买 nike

我有一个通用接口(interface) ICommandHandler<>这将有许多实现,每个实现用于处理 ICommand 的特定实现,例如:

public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }

当我得到一个 ICommand对象我正在尝试将其动态分派(dispatch)到正确的 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 中。如果我重新抛出它,我将丢失堆栈跟踪。

我找到了一种通过创建委托(delegate)并使用 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);
}
}

最佳答案

大多数 DI 容器(包括 Ninject)允许你做这样的事情:

public void Dispatch<T>(T command) where T : ICommand
{
ICommandHandler<T> handler = IoC.Get<ICommandHandler<T>>();
handler.Handle(command);
}

如果您不知道命令的类型(换句话说,如果 typeof(T) != command.GetType()),使用双分派(dispatch)是最简单的方法:

class SomeCommand : ICommand
{
// ...

public void Dispatch(IoC ioc)
{
var handler = ioc.Get<IHandle<SomeCommand>>();
handler.Handle(this);
}
}

但是如果您发现将此代码添加到所有命令令人反感,您可以反射(reflection)一下。

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

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();
}

关于c# - 在通用目标上动态调用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15930670/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com