); cache.Add(parameters.Single()。ParameterType,handler); } }
var cmdA = new CommandA(); VAR CMDB </跨度> = new CommandB();
cache [cmdA.GetType ()](cmdA); //应该写’命令A处理’ 缓存[cmdB.GetType()]( CMDB </跨度> ); //应该写’命令B处理’}
CreateDelegate方法失败,出现System.ArgumentException:’无法绑定到
我会单独封装你的缓存;您可以通过方法组添加特定的委托。例如:
public class HandlerCache { private readonly Dictionary<Type, HandlerDelegate> handlers = new Dictionary<Type, HandlerDelegate>(); public void AddHandler<T>(Action<T> action) where T : ICommand => handlers.Add(typeof(T), command => action((T) command); public void HandleCommand(ICommand command) { var commandType = command.GetType(); if (!handlers.TryGetValue(commandType, out var handler)) { throw new ArgumentException($"Unable to handle commands of type {commandType}"); } handler(command); } }
然后你可以像这样使用它:
var cache = new HandlerCache(); // This bit could be done by reflection if you want var handlerGroup = new HandlerGroup(); cache.AddHandler(handlerGroup.HandleA); cache.AddHandler(handlerGroup.HandleB); HandlerDelegate handler = cache.HandleCommand; // Use handler however you want
显然,总会有错误的余地 - 总会有可能会传递一个你无法处理的命令。请注意,这当前不处理继承,因为如果有一个派生自的类 CommandA ,那将是不匹配的 HandleCommand ;如果你想找到最具体的可用处理程序,你可能会通过基类型等。
CommandA
HandleCommand
如果通过反射将处理程序添加到缓存中,这种方法意味着您可以在不添加任何属性的情况下确定类型 - 您只需要确定适当的参数类型并将方法添加到缓存中。和往常一样,反射代码写起来有点烦人,但至少你可以用最少的努力添加新的处理程序。
铸造自 Action<CommandA> 至 Action<ICommand> 不是类型安全的,因为前者只接受 CommandA ,而后者接受任何 ICommand , 如 CommandB 。是因为
Action<CommandA>
Action<ICommand>
ICommand
CommandB
Delegate.CreateDelegate(typeof(HandlerDelegate), ...)
也失败了,因为签名 HandleA (和 HandleB )是不相容的 HandlerDelegate 。
HandleA
HandleB
HandlerDelegate
解决它的一种方法是使用正确的类型构造委托并动态调用它 DynamicInvoke , 例如:
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);
但更好的方法是创建表达式树并将其编译为委托:
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);