Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.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# 如何解析和执行命令行样式的字符串?_C#_.net_Asp.net Mvc_Object_Methods - Fatal编程技术网

C# 如何解析和执行命令行样式的字符串?

C# 如何解析和执行命令行样式的字符串?,c#,.net,asp.net-mvc,object,methods,C#,.net,Asp.net Mvc,Object,Methods,最后我有一个具体的问题,但我想提供大量的背景和背景,以便读者能够理解我的目标 背景 我正在用ASP.NET MVC 3构建一个控制台风格的应用程序。概念本身很简单:从客户端接收命令字符串,检查提供的命令是否存在,以及随命令提供的参数是否有效,执行命令,返回一组结果 内部工作 有了这个应用程序,我决定变得有点创造性。终端风格应用程序最明显的解决方案是构建世界上最大的IF语句。通过IF语句运行每个命令,并从内部调用适当的函数。我不喜欢这个主意。在旧版本的应用程序中,这就是它的运行方式,这是一个巨大的

最后我有一个具体的问题,但我想提供大量的背景和背景,以便读者能够理解我的目标

背景 我正在用ASP.NET MVC 3构建一个控制台风格的应用程序。概念本身很简单:从客户端接收命令字符串,检查提供的命令是否存在,以及随命令提供的参数是否有效,执行命令,返回一组结果

内部工作 有了这个应用程序,我决定变得有点创造性。终端风格应用程序最明显的解决方案是构建世界上最大的IF语句。通过IF语句运行每个命令,并从内部调用适当的函数。我不喜欢这个主意。在旧版本的应用程序中,这就是它的运行方式,这是一个巨大的混乱。向应用程序添加功能是非常困难的

经过深思熟虑,我决定构建一个称为命令模块的自定义对象。我们的想法是用每个请求构建这个命令模块。模块对象将包含所有可用的命令作为方法,然后站点将使用反射来检查用户提供的命令是否与方法名称匹配。命令模块对象位于一个名为
ICommandModule
的接口后面,如下所示

namespace U413.Business.Interfaces
{
    /// <summary>
    /// All command modules must ultimately inherit from ICommandModule.
    /// </summary>
    public interface ICommandModule
    {
        /// <summary>
        /// The method that will locate and execute a given command and pass in all relevant arguments.
        /// </summary>
        /// <param name="command">The command to locate and execute.</param>
        /// <param name="args">A list of relevant arguments.</param>
        /// <param name="commandContext">The current command context.</param>
        /// <param name="controller">The current controller.</param>
        /// <returns>A result object to be passed back tot he client.</returns>
        object InvokeCommand(string command, List<string> args, CommandContext commandContext, Controller controller);
    }
}
此行定义接受所需ID号、可选页码和带有回复值的可选回复命令的TOPIC命令

我目前实现的命令方法如下(方法上方的属性用于帮助菜单信息。帮助命令使用反射来读取所有这些内容并显示有组织的帮助菜单):

那么我很乐意这样做:

  • 从客户端接收命令字符串
  • 将命令字符串直接传递到
    ICommandModule
    上的
    InvokeCommand()
  • InvokeCommand()
    执行一些神奇的解析和反射,用正确的参数选择正确的命令方法,并调用该方法,只传递必要的参数
  • 理想实现的困境 我不知道该如何构建这种逻辑。我已经搔头好几天了。我希望我有第二双眼睛来帮助我解决这个问题(因此,我最终求助于一本关于所谓问题的小说)。事情应该按什么顺序发生

    我是否应该拉出命令,找到所有具有该命令名的方法,然后遍历所有可能的参数,然后遍历命令字符串的参数?我如何确定什么是成对的,什么是成对的。例如,如果我在命令字符串中循环并找到
    Reply“Reply”
    如何将Reply内容与Reply变量配对,同时遇到
    编号并将其提供给
    Id
    参数

    我肯定我现在把你搞糊涂了。让我用一些用户可能传入的命令字符串示例来说明:

    TOPIC 36 reply // Should prompt the user to enter reply text.
    TOPIC 36 reply "Hey what's up?" // Should post a reply to the topic.
    TOPIC 36 // Should display page 1 of the topic.
    TOPIC 36 page 4 // Should display page 4 of the topic.
    
    我如何知道向
    Id
    参数发送36?我如何知道将reply与“Hey what's up?”配对,并将“Hey what's up?”作为方法上reply参数的值传递

    为了知道调用哪个方法重载,我需要知道提供了多少个参数,以便我可以将该数字与接受相同数量参数的命令方法重载相匹配。问题是,`主题36回答“嘿,怎么了?”实际上是两个论点,而不是三个作为回答和“嘿…”作为一个论点

    我不介意将
    InvokeCommand()
    方法膨胀一点(或很多),只要这意味着所有复杂的解析和反射都在那里处理,并且我的命令方法可以保持良好、干净和易于编写

    我想我真的只是在这里寻找一些见解。有人有什么创造性的想法来解决这个问题吗?这确实是一个大问题,因为IF语句的参数使得为应用程序编写新命令变得非常复杂。命令是应用程序的一部分,我希望它非常简单,以便可以轻松地扩展和更新。以下是我的应用程序中实际的TOPIC命令方法:

        /// <summary>
        /// Shows a topic and all replies to that topic.
        /// </summary>
        /// <param name="args">A string list of user-supplied arguments.</param>
        [CommandInfo("Displays a topic and its replies.")]
        [CommandArgInfo("ID", "Specify topic ID to display the topic and all associated replies.", true, 0)]
        [CommandArgInfo("Page#/REPLY/EDIT/DELETE [Reply ID]", "Subcommands can be used to navigate pages, reply to the topic, edit topic or a reply, or delete topic or a reply.", false, 1)]
        public void TOPIC(List<string> args)
        {
            if ((args.Count == 1) && (args[0].IsLong()))
                TOPIC_Execute(args);
            else if ((args.Count == 2) && (args[0].IsLong()))
                if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
                    TOPIC_ReplyPrompt(args);
                else if (args[1].ToLower() == "edit")
                    TOPIC_EditPrompt(args);
                else if (args[1].ToLower() == "delete")
                    TOPIC_DeletePrompt(args);
                else
                    TOPIC_Execute(args);
            else if ((args.Count == 3) && (args[0].IsLong()))
                if ((args[1].ToLower() == "edit") && (args[2].IsLong()))
                    TOPIC_EditReplyPrompt(args);
                else if ((args[1].ToLower() == "delete") && (args[2].IsLong()))
                    TOPIC_DeleteReply(args);
                else if (args[1].ToLower() == "edit")
                    TOPIC_EditExecute(args);
                else if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
                    TOPIC_ReplyExecute(args);
                else if (args[1].ToLower() == "delete")
                    TOPIC_DeleteExecute(args);
                else
                    _result.DisplayArray.Add(DisplayObject.InvalidArguments);
            else if ((args.Count >= 3) && (args[0].IsLong()))
                if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
                    TOPIC_ReplyExecute(args);
                else if ((args[1].ToLower() == "edit") && (args[2].IsLong()))
                    TOPIC_EditReplyExecute(args);
                else if (args[1].ToLower() == "edit")
                    TOPIC_EditExecute(args);
                else
                    _result.DisplayArray.Add(DisplayObject.InvalidArguments);
            else
                _result.DisplayArray.Add(DisplayObject.InvalidArguments);
        }
    
    如您所见,我并不担心如何找到命令方法,因为它已经在工作了。我正在考虑一种解析命令字符串、组织参数,然后使用这些信息选择正确的命令方法/使用反射重载的好方法

    最终设计目标 我正在寻找一种非常好的方法来解析我传递的命令字符串。我希望解析器能够识别以下几点:

    • 选项。标识命令字符串中的选项

    • 名称/值对。识别名称/值对(例如,[page#]我通常使用的解决方案是这样的。请忽略我的语法错误…我使用C#已经有几个月了。基本上,用
      System.Collections.Generic.Dictionary
      查找和虚拟函数调用替换if/else/switch

      interface ICommand
      {
          string Name { get; }
          void Invoke();
      }
      
      //Example commands
      class Edit : ICommand
      {
          string Name { get { return "edit"; } }
          void Invoke()
          {
              //Do whatever you need to do for the edit command
          }
      }
      
      class Delete : ICommand
      {
          string Name { get { return "delete"; } }
          void Invoke()
          {
              //Do whatever you need to do for the delete command
          }
      }
      
      class CommandParser
      {
          private Dictionary<string, ICommand> commands = new ...;
      
          public void AddCommand(ICommand cmd)
          {
              commands.Insert(cmd.Name, cmd);
          }
      
          public void Parse(string commandLine)
          {
              string[] args = SplitIntoArguments(commandLine); //Write that method yourself :)
              foreach(string arg in args)
              {
                  ICommand cmd = commands.Find(arg);
                  if (!cmd)
                  {
                      throw new SyntaxError(String.Format("{0} is not a valid command.", arg));
                  }
                  cmd.Invoke();
              }
          }
      }
      
      class CommandParserXyz : CommandParser
      {
          CommandParserXyz()
          {
              AddCommand(new Edit);
              AddCommand(new Delete);
          }
      }
      
      接口ICommand
      {
      字符串名称{get;}
      void Invoke();
      }
      //示例命令
      类编辑:ICommand
      {
      字符串名称{get{返回“编辑”;}
      void Invoke()
      {
      //执行编辑命令所需的任何操作
      }
      }
      类删除:ICommand
      {
      字符串名称{get{返回“delete”;}
      void Invoke()
      {
      //执行delete命令所需的任何操作
      }
      }
      类命令分析器
      {
      私有字典命令=新建。。。;
      公共void AddCommand(ICommand cmd)
      {
      commands.Insert(cmd.Name,cmd);
      }
      公共void解析(字符串命令行)
      {
      string[]args=splitintarguments(命令行);//自己编写该方法:)
      foreach(args中的字符串arg)
      {
      ICommand cmd=commands.Find(arg);
      如果(!cmd)
      {
      抛出新的SyntaxError(String.Format(“{0}”不是有效的命令。”,arg
      
      TOPIC 36 reply // Should prompt the user to enter reply text.
      TOPIC 36 reply "Hey what's up?" // Should post a reply to the topic.
      TOPIC 36 // Should display page 1 of the topic.
      TOPIC 36 page 4 // Should display page 4 of the topic.
      
          /// <summary>
          /// Shows a topic and all replies to that topic.
          /// </summary>
          /// <param name="args">A string list of user-supplied arguments.</param>
          [CommandInfo("Displays a topic and its replies.")]
          [CommandArgInfo("ID", "Specify topic ID to display the topic and all associated replies.", true, 0)]
          [CommandArgInfo("Page#/REPLY/EDIT/DELETE [Reply ID]", "Subcommands can be used to navigate pages, reply to the topic, edit topic or a reply, or delete topic or a reply.", false, 1)]
          public void TOPIC(List<string> args)
          {
              if ((args.Count == 1) && (args[0].IsLong()))
                  TOPIC_Execute(args);
              else if ((args.Count == 2) && (args[0].IsLong()))
                  if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
                      TOPIC_ReplyPrompt(args);
                  else if (args[1].ToLower() == "edit")
                      TOPIC_EditPrompt(args);
                  else if (args[1].ToLower() == "delete")
                      TOPIC_DeletePrompt(args);
                  else
                      TOPIC_Execute(args);
              else if ((args.Count == 3) && (args[0].IsLong()))
                  if ((args[1].ToLower() == "edit") && (args[2].IsLong()))
                      TOPIC_EditReplyPrompt(args);
                  else if ((args[1].ToLower() == "delete") && (args[2].IsLong()))
                      TOPIC_DeleteReply(args);
                  else if (args[1].ToLower() == "edit")
                      TOPIC_EditExecute(args);
                  else if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
                      TOPIC_ReplyExecute(args);
                  else if (args[1].ToLower() == "delete")
                      TOPIC_DeleteExecute(args);
                  else
                      _result.DisplayArray.Add(DisplayObject.InvalidArguments);
              else if ((args.Count >= 3) && (args[0].IsLong()))
                  if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
                      TOPIC_ReplyExecute(args);
                  else if ((args[1].ToLower() == "edit") && (args[2].IsLong()))
                      TOPIC_EditReplyExecute(args);
                  else if (args[1].ToLower() == "edit")
                      TOPIC_EditExecute(args);
                  else
                      _result.DisplayArray.Add(DisplayObject.InvalidArguments);
              else
                  _result.DisplayArray.Add(DisplayObject.InvalidArguments);
          }
      
          /// <summary>
          /// Invokes the specified command method and passes it a list of user-supplied arguments.
          /// </summary>
          /// <param name="command">The name of the command to be executed.</param>
          /// <param name="args">A string list of user-supplied arguments.</param>
          /// <param name="commandContext">The current command context.</param>
          /// <param name="controller">The current controller.</param>
          /// <returns>The modified result object to be sent to the client.</returns>
          public object InvokeCommand(string command, List<string> args, CommandContext commandContext, Controller controller)
          {
              _result.CurrentContext = commandContext;
              _controller = controller;
      
              MethodInfo commandModuleMethods = this.GetType().GetMethod(command.ToUpper());
              if (commandModuleMethods != null)
              {
                  commandModuleMethods.Invoke(this, new object[] { args });
                  return _result;
              }
              else
                  return null;
          }
      
      // Metadata to be used by the HELP command when displaying HELP menu, and by the
      // command string parser when deciding what types of arguments to look for in the
      // string. I want to place these above the first overload of a command method.
      // I don't want to do an attribute on each argument as some arguments get passed
      // into multiple overloads, so instead the attribute just has a name property
      // that is set to the name of the argument. Same name the user should type as well
      // when supplying a name/value pair argument (e.g. Page 3).
      
      [CommandInfo("Test command tests things.")]
      [ArgInfo(
          Name="ID",
          Description="The ID of the topic.",
          ArgType=ArgType.ValueOnly,
          Optional=false
          )]
      [ArgInfo(
          Name="PAGE",
          Description="The page number of the topic.",
          ArgType=ArgType.NameValuePair,
          Optional=true
          )]
      [ArgInfo(
          Name="REPLY",
          Description="Context shortcut to execute a reply.",
          ArgType=ArgType.NameValuePair,
          Optional=true
          )]
      [ArgInfo(
          Name="OPTIONS",
          Description="One or more options.",
          ArgType=ArgType.MultiOption,
          Optional=true
          PossibleValues=
          {
              { "-S", "Sort by page" },
              { "-R", "Refresh page" },
              { "-F", "Follow topic." }
          }
          )]
      [ArgInfo(
          Name="SUBCOMMAND",
          Description="One of several possible subcommands.",
          ArgType=ArgType.SingleOption,
          Optional=true
          PossibleValues=
          {
              { "NEXT", "Advance current page by one." },
              { "PREV", "Go back a page." },
              { "FIRST", "Go to first page." },
                  { "LAST", "Go to last page." }
          }
          )]
      public void TOPIC(int id)
      {
          // Example Command String: "TOPIC 13"
      }
      
      public void TOPIC(int id, int page)
      {
          // Example Command String: "TOPIC 13 page 2"
      }
      
      public void TOPIC(int id, string reply)
      {
          // Example Command String: TOPIC 13 reply "reply"
      
          // Just a shortcut argument to another command.
          // Executes actual reply command.
          REPLY(id, reply, { "-T" });
      }
      
      public void TOPIC(int id, List<string> options)
      {
          // options collection should contain a list of supplied options
      
          Example Command String: "TOPIC 13 -S",
                                  "TOPIC 13 -S -R",
                                  "TOPIC 13 -R -S -F",
                                  etc...
      }
      
      interface ICommand
      {
          string Name { get; }
          void Invoke();
      }
      
      //Example commands
      class Edit : ICommand
      {
          string Name { get { return "edit"; } }
          void Invoke()
          {
              //Do whatever you need to do for the edit command
          }
      }
      
      class Delete : ICommand
      {
          string Name { get { return "delete"; } }
          void Invoke()
          {
              //Do whatever you need to do for the delete command
          }
      }
      
      class CommandParser
      {
          private Dictionary<string, ICommand> commands = new ...;
      
          public void AddCommand(ICommand cmd)
          {
              commands.Insert(cmd.Name, cmd);
          }
      
          public void Parse(string commandLine)
          {
              string[] args = SplitIntoArguments(commandLine); //Write that method yourself :)
              foreach(string arg in args)
              {
                  ICommand cmd = commands.Find(arg);
                  if (!cmd)
                  {
                      throw new SyntaxError(String.Format("{0} is not a valid command.", arg));
                  }
                  cmd.Invoke();
              }
          }
      }
      
      class CommandParserXyz : CommandParser
      {
          CommandParserXyz()
          {
              AddCommand(new Edit);
              AddCommand(new Delete);
          }
      }
      
          static Dictionary<string, Action<List<string>>> commandMapper;
      
          static void Main(string[] args)
          {
              InitMapper();
      
              Invoke("TOPIC", new string[]{"1","2","3"}.ToList());
              Invoke("Topic", new string[] { "1", "2", "3" }.ToList());
              Invoke("Browse", new string[] { "1", "2", "3" }.ToList());
              Invoke("BadCommand", new string[] { "1", "2", "3" }.ToList());
          }
      
          private static void Invoke(string command, List<string> args)
          {
              command = command.ToLower();
              if (commandMapper.ContainsKey(command))
              {
                  // Execute the method
                  commandMapper[command](args);
              }
              else
              {
                  // Command not found
                  Console.WriteLine("{0} : Command not found!", command);
              }
          }
      
          private static void InitMapper()
          {
              // Add more command to the mapper here as you have more
              commandMapper = new Dictionary<string, Action<List<string>>>();
              commandMapper.Add("topic", Topic);
              commandMapper.Add("browse", Browse);
          }
      
          static void Topic(List<string> args)
          {
              // ..
              Console.WriteLine("Executing Topic");
          }
      
          static void Browse(List<string> args)
          {
              // ..
              Console.WriteLine("Executing Browse");
          }
      
      public void TOPIC (
          [ArgInfo("Specify topic ID...")] int Id, 
          [ArgInfo("Specify topic page...")] int? page) 
      {
          ...
      }
      
      string data = null;
      bool help   = false;
      int verbose = 0;
      var p = new OptionSet () {
          { "file=",      v => data = v },
          { "v|verbose",  v => { ++verbose } },
          { "h|?|help",   v => help = v != null },
      };
      List<string> extra = p.Parse (args);