C# 分析和处理命令

C# 分析和处理命令,c#,parsing,command-line,command,C#,Parsing,Command Line,Command,我正在为我的项目制作一个调试控制台,从文本框等获取输入 但是,我想知道,除了一个巨大的switch语句,是否还有其他选择: switch(command.ToLower) { case "example": // do things... break; case "example2": // do things... break; } 因为我觉得有一个更优雅的解决方案可用,但我的技能无法访问它 编辑: 由于@owlsoli的惊人贡献,我现在已经有了我的代码,我在

我正在为我的项目制作一个调试控制台,从
文本框
等获取输入

但是,我想知道,除了一个巨大的switch语句,是否还有其他选择:

switch(command.ToLower) {
case "example":
    // do things...
    break;
case "example2":
    // do things...
    break;
}
因为我觉得有一个更优雅的解决方案可用,但我的技能无法访问它

编辑: 由于@owlsoli的惊人贡献,我现在已经有了我的代码,我在下面发布了我提交的代码的修改版本,该版本正在为我运行。谢谢@OwlSolo你是一个传奇人物

class parseCommand
{
    public static commandBase commandInstance { get; set; }

    public static void parse(string command)
    {
        string[] tokens = command.Split(' '); // tokens[0] is the original command
        object[] parameters = tokens.Skip(1).ToArray();

        List<Type> cmdTypes = System.Reflection.Assembly
            .GetExecutingAssembly()
            .GetTypes()
            .Where(t => typeof(commandBase).IsAssignableFrom(t))
            .ToList();

        foreach(Type derivedType in cmdTypes)
        {
            if (derivedType.Name.ToLower() == tokens[0].ToLower())
            {
                commandInstance = (commandBase)Activator.CreateInstance(derivedType);
                commandInstance.parameters = parameters;
                commandInstance.Execute();
                break;
            }
        }
    }
}
类解析命令
{
公共静态commandBase commandInstance{get;set;}
公共静态void解析(string命令)
{
string[]tokens=command.Split(“”);//tokens[0]是原始命令
object[]parameters=tokens.Skip(1.ToArray();
列出cmdTypes=System.Reflection.Assembly
.getExecutionGassembly()
.GetTypes()
.Where(t=>typeof(commandBase).IsAssignableFrom(t))
.ToList();
foreach(cmdTypes中的类型derivedType)
{
if(derivedType.Name.ToLower()==令牌[0].ToLower())
{
commandInstance=(commandBase)Activator.CreateInstance(derivedType);
commandInstance.parameters=参数;
commandInstance.Execute();
打破
}
}
}
}

您可以执行以下操作:

var lookup = new Dictionary<string, YourParamType> ()  
{
  { "example", a  },
  { "example2", b },
   ....
};

YourParamType paraType;
if (lookup.TryGetValue(command.ToLower(), out para))   // TryGetValue, on popular      request
{       
   //your code
}
var lookup=新字典()
{
{“示例”,a},
{“示例2”,b},
....
};
副型副型;
if(lookup.TryGetValue(command.ToLower(),out para))//TryGetValue,根据流行请求
{       
//你的代码
}

不太可能。。。我要做的唯一一件事是将不同类型的命令拆分为不同的方法,使其更加精简/优雅,并使用泛型集合存储应用于每种类型的命令

例如:

List<string> MoveCommands = new List<string>(){"Move", "Copy", "Merge"};
List<string> Actions = new List<string>() {"Add", "Edit", "Delete"};

//.......

if(MoveCommands.contains(inputtext))
    ExecuteMoveCommand();
else if (Actions.contains(inputtext))
    ExecuteActionCommand();
List MoveCommands=new List(){“移动”、“复制”、“合并”};
列表操作=新建列表(){“添加”、“编辑”、“删除”};
//.......
if(MoveCommands.contains(inputtext))
ExecuteMoveCommand();
else if(Actions.contains(inputtext))
ExecuteActionCommand();

诸如此类的事情。。。您所走的路线只留下优雅和代码整洁的余地。

解析某种语言本身基本上是一门完整的学科,因此这个问题相当广泛。 语言词法分析器和解析器通常创建命令的树结构,这些命令在保留关键字和参数中分开。保留关键字包含例如实例命令。(如类似C语言的
开关
如果
转到
,等等) 问题是,这些命令的理想选择方式是使它们相互独立。这意味着关键字本身会引发一种非常不同的处理方式。通过参数进行微调

如果这适用于您的命令,那么您没有太多的选择来提供处理每个命令的独立方法。例如,JavaCC(JavaCompiler-Compiler)生成了一个代码库,其中包含了相当大的开关情况,从而生成了一个指令树。然后由用户评估提供的指令树,这通常是通过处理关键字的单个对象来完成的-因此可能有一个类
IfStatement
,它包含许多子指令并处理其执行

无论您在这里具体需要什么,真正的工作将是如何处理执行,而不是如何区分哪个命令调用哪个行为

您可能需要的结构可以如下所示:

abstract class BinaryCommand
{
    MyBaseCommand child1;
    MyBaseCommand child2;

    abstract object Execute();
}

class ExampleCommand1 : BinaryCommand
{
    override object Execute()
    {
         //DoStuff1...
    }
}

class ExampleCommand2 : BinaryCommand
{
    override object Execute()
    {
         //Do Somethign else
    }
}
"/namedArg:value1 value2"
至于区分关键字,有多种方法:

  • 一个大的switch语句

  • 持有一个
    字典
    ,从中查找处理命令的类型。例如:“Example1 abcde12345”将查找“Example1”,在字典中创建该类型的实例,并使用参数“abcde”和“12345”填充它

  • 一个相当大胆的方法是通过代码反映一个可以处理命令的类。
    您将拥有一个像
    IBaseCommand
    这样的接口,所有命令类都从该接口派生

然后通过以下方式解析整个过程:

// Assuming you're getting one command per line and one line is fed to this function
public void ParseCommands(string command)
{
    string[] tokens = command.Split(" ");
    // tokens[0] is the command name
    object[] parameters = (object[])tokens.Skip(1);//Take everything but the first element (you need to include LINQ for this)

    // Get all the types that derive from your base type
    List<Type> commandTypes = Assembly
        .GetExecutingAssembly()
        .GetTypes()
        .Where(t => typeof(IBaseCommand).IsAssignableFrom(t));

    foreach (Type derivedType in commandTypes)
    {
        if (derivedType.Name.ToLower == tokens[0].ToLower) 
        /* Here the class name needs to match the commandname; this yould also be a
           static property "Name" that is extracted via reflection from the classes for 
           instance, or you put all your commands in a Dictionary<String, Type> and lookup 
           tokens[0] in the Dictionary */
        {
            // Create an instance of the command type
            IBaseCommand myCommandInstance = Activator.CreateInstance(derivedType);
            myCommandInstance.parameters = parameters;
            myCommandInstance.Execute(); // Call the execute method, that knows what to do
                 break;
        }
    }   
}
//假设每行获得一条命令,并且一行被馈送到此函数
公共命令(字符串命令)
{
string[]tokens=command.Split(“”);
//令牌[0]是命令名
object[]parameters=(object[])tokens.Skip(1);//获取除第一个元素以外的所有元素(为此需要包含LINQ)
//获取从基类型派生的所有类型
List commandTypes=Assembly
.getExecutionGassembly()
.GetTypes()
其中(t=>typeof(IBaseCommand).IsAssignableFrom(t));
foreach(commandTypes中的类型derivedType)
{
if(derivedType.Name.ToLower==令牌[0].ToLower)
/*在这里,类名需要与commandname匹配;这也是一个
静态属性“Name”,通过反射从
实例,或者将所有命令放入字典并进行查找
字典中的标记[0]*/
{
//创建命令类型的实例
IBaseCommand myCommandInstance=Activator.CreateInstance(derivedType);
myCommandInstance.parameters=参数;
myCommandInstance.Execute();//调用Execute方法,该方法知道要做什么
打破
}
}   
}

您的目标是使用尽可能少的命令,并通过参数尽可能多地执行操作。

一年前,我解决了同样的问题。因此,我将以我的代码为例解释它是如何工作的,这样您将知道如何设计命令行解析器以及如何解决您的问题

因为您已经需要一个基类或接口
// Assuming you're getting one command per line and one line is fed to this function
public void ParseCommands(string command)
{
    string[] tokens = command.Split(" ");
    // tokens[0] is the command name
    object[] parameters = (object[])tokens.Skip(1);//Take everything but the first element (you need to include LINQ for this)

    // Get all the types that derive from your base type
    List<Type> commandTypes = Assembly
        .GetExecutingAssembly()
        .GetTypes()
        .Where(t => typeof(IBaseCommand).IsAssignableFrom(t));

    foreach (Type derivedType in commandTypes)
    {
        if (derivedType.Name.ToLower == tokens[0].ToLower) 
        /* Here the class name needs to match the commandname; this yould also be a
           static property "Name" that is extracted via reflection from the classes for 
           instance, or you put all your commands in a Dictionary<String, Type> and lookup 
           tokens[0] in the Dictionary */
        {
            // Create an instance of the command type
            IBaseCommand myCommandInstance = Activator.CreateInstance(derivedType);
            myCommandInstance.parameters = parameters;
            myCommandInstance.Execute(); // Call the execute method, that knows what to do
                 break;
        }
    }   
}
public abstract class Command
{
    protected Command(string name);
    public string Name { get; }
    public abstract void Execute(ArgumentEnumerator args);
}
"/namedArg:value1 value2"
public sealed class CommandContext : Command
{
    public CommandContext(string name);
    public CommandCollection Commands { get; }
    public override void Execute(ArgumentEnumerator args);
}

public sealed class CommandCollection : KeyedCollection<string, Command>
{
    public CommandCollection() 
        : base(StringComparer.OrdinalIgnoreCase) 
    { 
    } 

    protected override string GetKeyForItem(Command item)
    { 
        return item.Name;
    }

    public bool TryGetCommand(string name, out Command command)
    { 
        return Dictionary.TryGetValue(name, out command);
    }
}
public sealed class DelegateCommand : Command
{
    public DelegateCommand(Delegate method);
    public Delegate Method { get; }
    public override void Execute(ArgumentEnumerator args);
}
CommandContext root = new CommandContext(
    "root",
    new Command("multiply", new Action<int, int>(Multiplication)),
    new CommandContext(
        "somecontext",
        // ...
        )
    );
ArgumentEnumerator args = new ("add /x:6 /y:7");

root.Execute(args);

public static void Multiplication([Argument("x")] int x, [Argument("y")] int y)
{
    // No parsing, only logic
    int result = x * y;
}