摘要介绍
通过命令行字符串来方便快捷的调用 C# 中定义的方法
准备
- 添加 nuget 包: NullLib.CommandLine
使用方式
首先, 在 NullLib.CommandLine 中用于调用方法的最基本类型是 CommandObject, 它包含了方法的各种信息, 例如 MethodInfo, ParameterInfo, 以及属性.
然后, 你需要定义一个包含要调用方法的类, 在这个类中, 每一个将被调用的方法都应该有一个 Command 属性, 之后我们将用这个类型实例化一个 CommandObject 实例.
一个示例类型:
public class AppCommands{ [Command] public void HelloWorld() { Console.WriteLine("Hello world!"); }}实例化一个 CommandObject 对象, 然后循环执行指令.
using System;using NullLib.CommandLine;
class Program{ static CommandObject<AppCommands> AppCommandObject = new CommandObject<AppCommands>(); // 实例化一个 CommandObject 对象 static void Main(string[] args) { Console.WriteLine("Now input commands."); while (true) { Console.Write(">>> "); // 提示符 string cmdline = Console.ReadLine(); if (!AppCommandObject.TryExecuteCommand(cmdline, out var result)) { if (result != null) // 如果一个方法没有返回值, 则结果是 null. Console.WriteLine(result); } else { Console.WriteLine("Command execute failed."); } } }}运行程序, 并输入指令:
Now input commands.>>> HelloWorldHello world!为每一个参数指定 ArgumentConverter 以传递参数到方法.
那么, 我们再试试将这些方法添加到 AppCommands 中
[Command(typeof(FloatArguConverter), typeof(FloatArguConverter))] // NullLib.CommandLine 中的内置 ArgumentConverterpublic float Plus(float a, float b){ return a + b;}[Command(typeof(FloatArguConverter))] // 如果跟着的转换器与上一个是一样的, 那么你可以忽略它们public float Mul(float a, float b){ return a * b;}[Command(typeof(DoubleArguConverter))]public double Log(double n, double newBase = Math.E) // 你也可以使用可选参数{ return Math.Log(n, newBase);}[Command(typeof(ForeachArguConverter<FloatArguConverter>))] // 数组中的每一个字符串都将被 FloatArguConverter 转换public float Sum(params float[] nums) // 可变参数的方法也是受支持的{ float result = 0; foreach (var i in nums) result += i; return result;}[Command(typeof(ArgutConverter))] // 如果不需要做任何转换, 则可以指定一个 'ArguConverter'public void Print(string txt){ Console.WriteLine(txt);}[Command] // 默认的转换器是 'ArguConverter', 在这种情况下你可以忽略它们public bool StringEquals(string txt1, string txt2) // 或者指定 'null' 来使用上一个转换器(在这里指 ArguConverter){ return txt1.Equals(txt2);}[Command(typeof(EnumArguConverter<ConsoleColor>))] // EnumConverter 可以用来将字符串自动转换为枚举类型public void SetBackground(ConsoleColor color){ Console.BackgroundColor = color;}Run and input:
Now input commands.>>> Plus 1 12>>> Mul 2 48>>> Log 8 23>>> Log 82.07944154167984>>> Sum 1 2 3 410>>> Print "一些文本\t转义字符也是受支持的"一些文本 转义字符也是受支持的>>> StringEquals qwq awaFalse>>> SetBackground White>>> SetBackground 0更多
通过这个库, 你可以通过命令行字符串方便快捷的调用 C# 中定义的方法, 甚至于通过一些方法实现简单的, 类似于 CMD 命令提示符的命令解释器, 完整文档,
NullLib.CommandLine
通过命令行字符串来方便快捷的调用 C# 中定义的方法
使用方式
首先, 在 NullLib.CommandLine 中用于调用方法的最基本类型是 CommandObject, 它包含了方法的各种信息, 例如 MethodInfo, ParameterInfo, 以及属性.
然后, 你需要定义一个包含要调用方法的类, 在这个类中, 每一个将被调用的方法都应该有一个 Command 属性, 之后我们将用这个类型实例化一个 CommandObject 实例.
一个示例类型:
public class AppCommands{ [Command] public void HelloWorld() { Console.WriteLine("Hello world!"); }}实例化一个 CommandObject 对象, 然后循环执行指令.
using System;using NullLib.CommandLine;
class Program{ static CommandObject<AppCommands> AppCommandObject = new CommandObject<AppCommands>(); // 实例化一个 CommandObject 对象 static void Main(string[] args) { Console.WriteLine("Now input commands."); while (true) { Console.Write(">>> "); // 提示符 string cmdline = Console.ReadLine(); if (!AppCommandObject.TryExecuteCommand(cmdline, out var result)) { if (result != null) // 如果一个方法没有返回值, 则结果是 null. Console.WriteLine(result); } else { Console.WriteLine("Command execute failed."); } } }}运行程序, 并输入指令:
Now input commands.>>> HelloWorldHello world!为每一个参数指定 ArgumentConverter 来以传递参数到方法.
那么, 我们再试试将这些方法添加到 AppCommands 中
[Command(typeof(FloatArguConverter), typeof(FloatArguConverter))] // NullLib.CommandLine 中的内置 ArgumentConverterpublic float Plus(float a, float b){ return a + b;}[Command(typeof(FloatArguConverter))] // 如果跟着的转换器与上一个是一样的, 那么你可以忽略它们public float Mul(float a, float b){ return a * b;}[Command(typeof(DoubleArguConverter))]public double Log(double n, double newBase = Math.E) // 你也可以使用可选参数{ return Math.Log(n, newBase);}[Command(typeof(ForeachArguConverter<FloatArguConverter>))] // 数组中的每一个字符串都将被 FloatArguConverter 转换public float Sum(params float[] nums) // 可变参数的方法也是受支持的{ float result = 0; foreach (var i in nums) result += i; return result;}[Command(typeof(ArgutConverter))] // 如果不需要做任何转换, 则可以指定一个 'ArguConverter'public void Print(string txt){ Console.WriteLine(txt);}[Command] // 默认的转换器是 'ArguConverter', 在这种情况下你可以忽略它们public bool StringEquals(string txt1, string txt2) // 或者指定 'null' 来使用上一个转换器(在这里指 ArguConverter){ return txt1.Equals(txt2);}[Command(typeof(EnumArguConverter<ConsoleColor>))] // EnumConverter 可以用来将字符串自动转换为枚举类型public void SetBackground(ConsoleColor color){ Console.BackgroundColor = color;}Run and input:
Now input commands.>>> Plus 1 12>>> Mul 2 48>>> Log 8 23>>> Log 82.07944154167984>>> Sum 1 2 3 410>>> Print "一些文本\t转义字符也是受支持的"一些文本 转义字符也是受支持的>>> StringEquals qwq awaFalse>>> SetBackground White>>> SetBackground 0>>> Print "你可以通过一个枚举类型的名字或整数值来进行转换"你可以通过一个枚举类型的名字或整数值来进行转换Types
-
CommandAttribute:
可以通过命令行字符串执行的方法必须有一个
CommandAttribute属性 -
CommandObject:
用于使用命令行字符串调用方法的类
-
CommandInvoker:
帮助通过
IArguments调用方法 -
CommandParser:
帮助分析命令行字符串以调用方法
-
ArgumentConverter:
继承
IArgumentConverter接口, 抽象类, 应该被自定义转换器继承 -
ArgumentConverterManager:
帮助快速创建
ArgumentConverter -
CommandLineSegment:
命令行字符串的构成部分
-
ArgumentParser:
帮助将
CommandLineSegment分析为IArgument
CommandLineSegment
CommandLineSegment 是命令行字符串的组成部分
例如, 在 myprogram param1 "param2" 中, 有三个 CommandLineSegment:
- {Quoted: false, Content: “myprogram”}
- {Quoted: false, Content: “param1”}
- {Quoted: true, Content: ”\“param2\""}
将命令行字符串分割为 CommandLineSegment[], 使用 CommandParser.SplitCommandLine(string str)
Argument
命令的参数, 可有名称, 继承 IArgument, 当调用方法时, 将会被传递
ArgumentParser
下面是所有内置的 ArgumentParsers
-
ArguParser:
可将任何部分分析为一个
Argument -
IdentifierArguParser:
可将一个标识符部分分析为一个
Argument. 对应的正则表达式是 “{A-Za-z_}{A-Za-z0-9_}*” -
StringArguParser:
可以将任何 Quoted(被双引号包围的) 部分分析为
Argument -
FieldArguParser:
可以将像 name=value 的一个部分, 或者像 name= value 的两个部分分析为一个
Argument, 另外, 你可以指定分隔符, 默认分隔符是 ’=’ -
PropertyArguParser:
可以将像 -name value 的两个部分分析为
Argument, 并且你也可以指定 name 的起始字符串, 默认是 ”-“
ArgumentConverter
Here is all build-in ArgumentConverter:
-
ArguConverter:
不会做任何转换而直接返回源值的默认的
ArgumentConverter -
BoolArguConverter:
帮助转换到布尔值, 使用 bool.Parse 和 bool.TryParse
-
CharArguConverter:
帮助转换到字符, 仅当字符串有唯一一个字符时, 返回这个字符, 否则转换失败
-
ByteArguConverter:
-
ShortArguConverter:
-
IntArguConverter:
-
LongArguConverter:
-
FloatArguConverter:
-
DoubleArguConverter:
-
BigIntArguConverter:
-
DecimalArguConverter:
上面提到的转换器均返回对应的数字类型, 并且它们都通过调用对应类型的
Parse和TryParse方法实现转换 -
EnumArguConverter<T>:
帮助转换到枚举类型, T 应该被指定为一个枚举类型. 它可以从枚举值的名称或数字值来转换
-
ForeachArguConverter<TConverter>:
帮助转换到一个数组, 仅用于可变参数(使用
params修饰),TConverter必须实现IArgumentConverter接口, 每个值都将被指定的转换器转换, 最终得到一个对应类型的数组. -
CharArrayArguConverter:
帮助转换到字符数组, 它调用
string.ToCharArray()来进行转换.
About ArgumentParser
Custom Parser:
定义自定义的 ArgumentParser, 你需要遵守下面的规则:
- 实现
IArgumentParser接口 - 在分析完毕后, 引用参数
index必须离开参数结果(IArgument)的区域, 例如, 在索引 3, 在你的自定义分析器中, 他将返回结果 (结果被成功分析), 并且分析结果来自于两个CommandLineSegment, 所以index必须是 5 (在 3 和 4 之外).
About ArgumentConverter
自定义 Converter:
定义自定义 ArgumentConverter 的推荐方式是这样:
class MyConverter : ArgumentConverter<MyType> // 继承 ArgumentConverter<T> 而不是 IArgumentConverter<T>{ public override MyType Convert(string argument) { // 你的代码 } public override bool TryConvert(string argument, out MyType result) { // 你的代码 }}继承 ArgumentConverter<T> 而不是 IArgumentConverter<T> 的原因是, 在 ArgumentConverter<T> 中, 所有方法重载均调用这两个方法:
- T Convert(string argument);
- bool TryConverter(string argument, out T result);
所以如果继承 ArgumentConverter<T>, 你只需要重写这两个方法.
Tips:
- 不要使用 new 表达式来创建一个
ArgumentConverter实例, 请使用ArgumentConverterManager.GetConverter<T>().
FAQ
-
当我调用
CommandObject.ExecuteCommand(IArgumentParser[] parsers, string cmdline)时, 某些分析器不能正常使用:// 你必须以正确的顺序指定分析器, 否则就会这样:CommandObject<AppCommands> myCmds = new CommandObject<AppCommands>();myCmds.ExecuteCommand(new IArgumentParser[]{new ArguParser(), // 在这种情况下, FieldArguParser 和 PropertyArguParser 将不起作用.new FieldArguParser(), // 这是因为 ArguParser 可以分析任何 CommandLineSegments, 所以你应该new PropertyArguParser() // 将 ArguParser 作为最后一个分析器, 这样分析的时候就会先使用前两个}, Console.ReadLine());