gpt4 book ai didi

c# - 从具有比委托(delegate)签名更多派生参数的 MethodInfo 创建委托(delegate)

转载 作者:行者123 更新时间:2023-11-30 16:41:15 26 4
gpt4 key购买 nike

当目标方法参数是从委托(delegate)签名中指定的参数类型派生的类型时,我不知道如何创建委托(delegate)。这可能吗?我的项目涉及为许多不同的命令创建命令处理程序。这些命令按组排列,每个组都有自己的属性和依赖项(为简洁起见,我在这里省略了这些。)我试图从它们的组中提取那些处理程序方法(标有“Handler”属性),从结果中创建委托(delegate)MethodInfo,并将它们存储在字典中,键入命令类型。大致思路如下:

public delegate void HandlerDelegate(ICommand command);

public class Handler: Attribute
{
}

public interface ICommand
{
}

public class CommandA: ICommand
{
public string CmdASpecific = "Command A";
}

public class CommandB: ICommand
{
public string CmdBSpecific = "Command B";
}

public class HandlerGroup
{
[Handler]
public void HandleA(CommandA command)
{
Console.WriteLine($"{command.CmdASpecific} Handled");
}

[Handler]
public void HandleB(CommandB command)
{
Console.WriteLine($"{command.CmdBSpecific} Handled");
}
}

我通过反射扫描一个 HandlerGroup 实例,提取具有“Handler”属性的方法,从中创建一个委托(delegate)并将它们添加到字典(由处理程序期望的参数类型键入)以供稍后调用:

public void Main(){
var handlerGroup = new HandlerGroup();

Dictionary<Type, HandlerDelegate> cache = new Dictionary<Type, HandlerDelegate>();

foreach (var handlerInfo in handlerGroup.GetType().GetMethods())
{
if ((Handler)handlerInfo.GetCustomAttribute(typeof(Handler), false) is Handler handlerAttribute)
{
var parameters = handlerInfo.GetParameters();
HandlerDelegate handler = (HandlerDelegate)Delegate.CreateDelegate(typeof(HandlerDelegate), handlerGroup, handlerInfo.Name);
cache.Add(parameters.Single().ParameterType, handler);
}
}

var cmdA = new CommandA();
var cmdB = new CommandB();

cache[cmdA.GetType()](cmdA); //Should write 'Command A Handled'
cache[cmdB.GetType()](cmdB); //Should write 'Command B Handled'
}

CreateDelegate 方法失败并出现 System.ArgumentException:“无法绑定(bind)到目标方法,因为它的签名或安全透明度与委托(delegate)类型不兼容。”异常。

我可以通过向 Handler 属性添加一个属性来解决这个问题:

public class Handler: Attribute
{
public Type CommandType;
public Handler(Type commandType)
{
CommandType = commandType;
}
}

并改用这个 HandlerGroup:

public class HandlerGroup
{
[Handler(typeof(CommandA))]
public void HandleA(ICommand command)
{
var tmp = (CommandA)command;
Console.WriteLine($"{tmp.CmdASpecific} Handled");
}

[Handler(typeof(CommandB))]
public void HandleB(ICommand command)
{
var tmp = (CommandB)command;
Console.WriteLine($"{tmp.CmdBSpecific} Handled");
}
}

然后使用 Handler 属性的 CommandType 属性而不是处理程序方法的参数类型将其添加到缓存中:

            if (handlerInfo.GetCustomAttribute(typeof(Handler), false) is Handler handlerAttribute)
{
HandlerDelegate handler = (HandlerDelegate)Delegate.CreateDelegate(typeof(HandlerDelegate), handlerGroup, handlerInfo.Name);
cache.Add(handlerAttribute.CommandType, handler);
}

真的有其他选择吗?虽然这可行,但我真的不想依赖假设处理程序以某种方式实现。

最佳答案

类型转换自 Action<CommandA>Action<ICommand>不是类型安全的,因为前者只接受 CommandA ,而后者接受任何 ICommand ,例如 CommandB .为此

Delegate.CreateDelegate(typeof(HandlerDelegate), ...)

同样失败,因为 HandleA 的签名(和 HandleB )与 HandlerDelegate 不兼容.

解决它的一种方法是构造具有正确类型的委托(delegate)并使用 DynamicInvoke 动态调用它,例如:

var parameters = handlerInfo.GetParameters();
// constructing correct delegate here
var dynamicHandler = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(parameters.Single().ParameterType), handlerGroup, handlerInfo);
HandlerDelegate handler = (p) =>
{
// invoking
dynamicHandler.DynamicInvoke(p);
};
cache.Add(parameters.Single().ParameterType, handler);

但更好的方法是创建表达式树并将其编译为委托(delegate):

var parameters = handlerInfo.GetParameters();
// expression of type ICommand
var expressionArg = Expression.Parameter(typeof(ICommand), "x");
// this is handlerInfo.HandleA((CommandA) x)
var callExp = Expression.Call(Expression.Constant(handlerGroup), handlerInfo, Expression.Convert(expressionArg, parameters.Single().ParameterType));
// this is delegate x => handlerInfo.HandleA((CommandA) x)
var handler = Expression.Lambda<HandlerDelegate>(callExp, new[] { expressionArg }).Compile();
cache.Add(parameters.Single().ParameterType, handler);

关于c# - 从具有比委托(delegate)签名更多派生参数的 MethodInfo 创建委托(delegate),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49710440/

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