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;
}