C# 创建操作<;T>;其中T是方法参数的基类
我正在尝试做一些类似CommandBus的事情,在处理命令时应该调用一个方法。我把这些储存在录音机里C# 创建操作<;T>;其中T是方法参数的基类,c#,delegates,C#,Delegates,我正在尝试做一些类似CommandBus的事情,在处理命令时应该调用一个方法。我把这些储存在录音机里 private readonly ConcurrentDictionary<Type, Action<BaseCommand>> _commandHandlers = new ConcurrentDictionary<Type, Action<BaseCommand>>(); StartCommand继承BaseCommand 我正试图用这段代码
private readonly ConcurrentDictionary<Type, Action<BaseCommand>> _commandHandlers = new ConcurrentDictionary<Type, Action<BaseCommand>>();
StartCommand继承BaseCommand
我正试图用这段代码填充口述
var commands = new List<Type>();
//-- Get all commands that is defined in assembly
var tmpAssembly = typeof(CommandBus).Assembly;
commands.AddRange(tmpAssembly.GetTypes().Where(t => t.BaseType == typeof(BaseCommand)));
commands.ForEach(c =>
{
var methodInfo = instance.GetType().GetMethods().SingleOrDefault(m => m.GetParameters().Count() == 1
&& m.GetParameters().Any(p => p.ParameterType == c));
if (methodInfo != null)
{
var action = (Action<BaseCommand>)Delegate.CreateDelegate(typeof(Action<BaseCommand>), instance, methodInfo);
if (!_commandHandlers.TryAdd(c, action))
throw new ArgumentException(string.Format("An CommandHandler is already registered for the command: '{0}'. Only one CommandHandler can be registered for a command", c.Name));
}
});
var命令=新列表();
//--获取在程序集中定义的所有命令
var tmpAssembly=typeof(CommandBus).Assembly;
commands.AddRange(tmpAssembly.GetTypes().Where(t=>t.BaseType==typeof(BaseCommand));
commands.ForEach(c=>
{
var methodInfo=instance.GetType().GetMethods().SingleOrDefault(m=>m.GetParameters().Count()==1
&&m.GetParameters().Any(p=>p.ParameterType==c);
如果(methodInfo!=null)
{
var action=(action)Delegate.CreateDelegate(typeof(action)、instance、methodInfo);
if(!\u commandHandlers.TryAdd(c,action))
抛出新的ArgumentException(string.Format(“已为命令“{0}”注册了CommandHandler。一个命令只能注册一个CommandHandler”,c.Name));
}
});
当我运行这段代码时,我得到以下异常:无法绑定到目标方法,因为它的签名或安全透明性与委托类型的签名或安全透明性不兼容
当然,这是正确的,因为我的方法不是将BaseCommand作为参数,而是将StartCommand
但是有没有什么方法可以让我创造动作呢?我已经用表达式查看了som示例,但我没有设法弄明白
提前感谢我发现这个问题有点让人困惑,原因有两个:
StartCommand
的实例方法本身需要另一个StartCommand
实例来传递给它表达式
类是正确的。您可以创建一个表达式,该表达式将显式转换为调用成员所需的类型,以便委托实例本身可以接收基类型
例如:
/// <summary>
/// Create an Action<T> delegate instance which will call the
/// given method, using the given instance, casting the argument
/// of type T to the actual argument type of the method.
/// </summary>
/// <typeparam name="T">The type for the delegate's parameter</typeparam>
/// <param name="b">The instance of the object for the method call</param>
/// <param name="miCommand">The method to call</param>
/// <returns>A new Action<T></returns>
private static Action<T> CreateAction<T>(B b, MethodInfo miCommand)
{
// Create the parameter object for the expression, and get
// the type needed for it
ParameterExpression tParam = Expression.Parameter(typeof(T));
Type parameterType = miCommand.GetParameters()[0].ParameterType;
// Create an expression to cast the parameter to the correct type
// for the call
Expression castToType = Expression.Convert(tParam, parameterType, null);
// Create the delegate itself: compile a lambda expression where
// the lambda calls the method miCommand using the instance b and
// passing the result of the cast expression as the argument.
return (Action<T>)Expression.Lambda(
Expression.Call(
Expression.Constant(b, b.GetType()),
miCommand, castToType),
tbParam).Compile();
}
//
///创建一个ActionT>delegate实例,该实例将调用
///给定方法,使用给定实例,强制转换参数
///类型T到方法的实际参数类型。
///
///委托参数的类型
///方法调用的对象的实例
///要调用的方法
///新行动t>
私有静态操作CreateAction(B,MethodInfo微命令)
{
//为表达式创建参数对象,并获取
//它所需要的类型
ParameterExpression tParam=表达式.参数(typeof(T));
类型parameterType=miCommand.GetParameters()[0]。parameterType;
//创建表达式以将参数强制转换为正确的类型
//打电话
表达式castToType=Expression.Convert(tParam,parameterType,null);
//创建委托本身:编译lambda表达式,其中
//lambda使用实例b和
//将强制转换表达式的结果作为参数传递。
返回(操作)表达式.Lambda(
表情,打电话(
Expression.Constant(b,b.GetType()),
miCommand,castToType),
tbParam.Compile();
}
您可以这样使用:
var action = CreateAction<BaseCommand>(instance, methodInfo);
if (!_commandHandlers.TryAdd(c, action))
throw new ArgumentException(string.Format("An CommandHandler is already registered for the command: '{0}'. Only one CommandHandler can be registered for a command", c.Name));
var action=CreateAction(实例,methodInfo);
if(!\u commandHandlers.TryAdd(c,action))
抛出新的ArgumentException(string.Format(“已为命令“{0}”注册了CommandHandler。一个命令只能注册一个CommandHandler”,c.Name));
用实际控制器类型替换类型名称B
。不幸的是,您的代码示例没有显示实例
的声明,因此我不能在这里使用真正的类型名
当然,在实际调用委托时,必须小心确保传递的实例类型正确!上述操作与执行强制转换类似,如果类型实际上不可强制转换为方法的参数类型,则会像强制转换一样引发异常。根据您的输入信息,我想建议一种不同的方法 与其尝试查看方法参数以查找绑定到命令处理程序的类型,不如在不同的类上拆分不同的命令处理程序。通过这种方式,您可以避免处理不同类型的委托的复杂性,更不用说您的代码更具可读性,更易于测试,等等 我向您介绍的想法是使用执行命令的标准方法存储类而不是方法 因此,首先我们创建一个用户属性,用于命令处理程序。通过这种方式,您只需将属性添加到类中,并使用要绑定到该类的命令类型
[System.AttributeUsage(System.AttributeTargets.Class)]
public class CommandTypeAttribute : Attribute
{
protected Type fCommandType;
public CommandTypeAttribute(Type commandType)
{
fCommandType = commandType;
}
public Type CommandType
{
get { return fCommandType; }
set { fCommandType = value; }
}
}
然后我们创建两个接口,一个用于命令,另一个用于命令处理程序。命令接口现在是空的,但您可以将其用于公开命令处理程序的公共数据/方法
public interface ICommandHandler
{
void Execute(ICommand data);
}
public interface ICommand
{
// common data for commands
}
我们为测试创建两个命令:
public class StartCommand : ICommand
{
//Start command data
}
public class OtherCommand : ICommand
{
//Other command data
}
然后,我们创建了我们的命令处理程序(现在您可以有许多您想要的,这是为了演示)。我们将只创建一个,以便能够测试当我们想要运行一个没有绑定到i的命令处理程序的命令时会发生什么
public class StartCommand : ICommand
{
//Start command data
}
public class OtherCommand : ICommand
{
//Other command data
}
[CommandTypeAttribute(typeof(StartCommand))]
public class StartCommandHandler : ICommandHandler
{
public StartCommandHandler()
{
}
public void Execute(ICommand data)
{
StartCommand scData = data as StartCommand;
if (scData == null)
throw new ArgumentException(string.Format("Invalid command for CommandHandler 'StartCommandHandler'. Expecting 'StartCommand' but received {0}", data == null ? "null" : data.GetType().Name));
//start command execution
}
}
public class CommandManager
{
private readonly ConcurrentDictionary<Type, ICommandHandler> fCommands = new ConcurrentDictionary<Type, ICommandHandler>();
public void FillCommandsList(Assembly[] commandDataAssemblies, Assembly[] commandAssemblies)
{
var data = new List<Type>();
foreach (Assembly assembly in commandDataAssemblies)
data.AddRange(assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(ICommand))));
var commands = new List<Type>();
foreach (Assembly assembly in commandAssemblies)
commands.AddRange(assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(ICommandHandler))));
foreach (Type dataType in data)
{
foreach (Type commandType in commands)
{
Type commandDataType = commandType.GetCustomAttribute<CommandTypeAttribute>().CommandType;
if (commandDataType == dataType)
{
if (!fCommands.ContainsKey(dataType))
{
fCommands[dataType] = (ICommandHandler)Activator.CreateInstance(commandType);
}
else
{
throw new ArgumentException(string.Format("A command handler is already registered for the command: '{0}'. Only one command handler can be registered for a command", dataType.Name));
}
}
}
}
}
public void ExecuteCommand(ICommand command)
{
if (command == null)
return;
Type commandType = command.GetType();
if (!fCommands.ContainsKey(commandType))
throw new ArgumentException(string.Format("Command '{0}' not found", commandType.Name));
ICommandHandler commandHandler = fCommands[commandType];
commandHandler.Execute(command);
}
}
public class TestSystem
{
public void Run()
{
CommandManager cm = new CommandManager();
cm.FillCommandsList(new Assembly[] { this.GetType().Assembly }, new Assembly[] { this.GetType().Assembly });
try
{
Console.WriteLine("Executing command StartCommand");
cm.ExecuteCommand(new StartCommand());
Console.WriteLine("Command executed with success");
}
catch (Exception ex)
{
Console.WriteLine("Exception raised.\n{0}", ex.Message);
}
try
{
Console.WriteLine("Executing command null");
cm.ExecuteCommand(null);
Console.WriteLine("Command executed with success");
}
catch (Exception ex)
{
Console.WriteLine("Exception raised.\n{0}", ex.Message);
}
try
{
Console.WriteLine("Executing command OtherCommand");
cm.ExecuteCommand(new OtherCommand());
Console.WriteLine("Command executed with success");
}
catch (Exception ex)
{
Console.WriteLine("Exception raised.\n{0}", ex.Message);
}
try
{
Console.WriteLine("Trying to add commands already in the list of commands");
cm.FillCommandsList(new Assembly[] { this.GetType().Assembly }, new Assembly[] { this.GetType().Assembly });
Console.WriteLine("Command add with success");
}
catch (Exception ex)
{
Console.WriteLine("Exception raised.\n{0}", ex.Message);
}
}
}
object[] atts = commandType.GetCustomAttributes(typeof(CommandTypeAttribute), true);
if (atts.Count() <= 0)
continue;
Type commandDataType = (atts[0] as CommandTypeAttribute).CommandType;
public static void Start(StartCommand command) { }
Action<BaseCommand> action = Start;
action(new EndCommand());
Action<BaseCommand> action = command => Start((StartCommand)command);
action(new EndCommand());
var methodInfo = ...;
if (methodInfo != null)
{
Action<BaseCommand> action = command => methodInfo.Invoke(null, command);
//...
}