Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 创建操作<;T>;其中T是方法参数的基类_C#_Delegates - Fatal编程技术网

C# 创建操作<;T>;其中T是方法参数的基类

C# 创建操作<;T>;其中T是方法参数的基类,c#,delegates,C#,Delegates,我正在尝试做一些类似CommandBus的事情,在处理命令时应该调用一个方法。我把这些储存在录音机里 private readonly ConcurrentDictionary<Type, Action<BaseCommand>> _commandHandlers = new ConcurrentDictionary<Type, Action<BaseCommand>>(); StartCommand继承BaseCommand 我正试图用这段代码

我正在尝试做一些类似CommandBus的事情,在处理命令时应该调用一个方法。我把这些储存在录音机里

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&lt;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&lt;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);
        //...
    }