使用委托进行命令行解析
使用委托进行命令行解析:委托支持一组常见的数据类型,并提供标准的语法验证。
引言
快速浏览该网站会发现,有几种不同类型的命令行解析器。其中一些相当不错。命令行解析是我们大多数程序员不假思索就会做的事情——当你意识到可以通过从命令行传递一两个选项来简化事情时,你已经开始编写你的控制台测试应用程序了——然后接下来你就开始噼里啪啦地敲代码了……
foreach (string arg in args) { if (arg.StartsWith ("-")) { blah, blah, blah... } }
大多数命令行程序都是为了测试库或进行快速概念验证而编写的。如果程序性质更持久,甚至是基于 GUI 的程序或提供启动参数的服务,您可能会考虑建立一个框架,允许结构化维护和添加命令行选项。这里描述的 ArgumentParser
类非常适合这个要求。
背景
几周前,我为我正在开发的一个程序编写了 ArgumentParser
库。我之前有一个程序手动解析参数,虽然代码已经足够干净,但随着我添加参数,它不断增长,直到最终控制台应用程序的最大部分是处理参数!这不一定不寻常,因为测试平台通常接口很精简。尽管如此,我发现自己做了些同样的事情——检查有效整数、构建消息等。总之,我对此感到很失望。
该库处理了许多事情,而不是由客户端程序处理:
- 捕获语法错误,例如预期的整数中包含字母字符
- 捕获参数错误,例如令牌后缺少参数
- 跟踪参数解析过程中错误的出现
- 在解析过程中保留所有错误和警告的文本
- 允许客户端程序添加警告或错误消息
Using the Code
命名空间 AsAbove.Command.Arguments
(ArgumentParser
是其中的一个成员)包含六个 delegate
定义。这些 delegate
s 定义了该库支持的数据类型。
public delegate string ArgumentString (string val);
public delegate string ArgumentInteger (int val);
public delegate string ArgumentDouble (double val);
public delegate string ArgumentBool (bool val);
public delegate string ArgumentDate (DateTime val);
public delegate string ArgumentSimple ();
例如,考虑一个接受两个参数的程序——一个 string
和一个 bool
ean 类型。该程序的目的是使用个人的社会安全号码来指示人员的活动工作状态的变化。该程序的命令行可能看起来像这样:
setactive -ssn 505-30-2773 -active-
命令的组成部分是程序名称 setactive
,社会安全号码令牌 -ssn
后跟号码,以及布尔参数 -active-
表示“不活动”。请注意,布尔参数必须始终以 +
或 -
符号结尾。如果令牌以 +
结尾,则计算结果为 true
,如果以 -
结尾,则计算结果为 false
。
string
、integer
、double
和 date
数据类型后面必须始终跟有该类型的参数;这些对应于上面名称中具有该数据类型的委托。delegate ArgumentSimple
不需要跟参数——当令牌出现在命令行上足以触发操作时使用它。例如,这个 delegate
对于请求使用 -h
或 -?
获得帮助很有用。
要为程序添加 ArgumentParser
支持,第一步是实现要为每个程序参数调用的 delegate
方法。对于 setactive
程序,我们将定义两个 delegate
s:
private string SSNDelegate (string val)
{
this.ssn_ = val;
return "";
}
private string ActiveDelegate (bool val)
{
this.active_ = val;
return "";
}
请注意,所有 delegate
s 都返回一个 string
。如果客户端程序需要将消息添加到程序输出中,它可以从 delegate
返回消息。
ArgumentParser 的参数验证
调用 delegate
时,参数的语法已经过验证。如果参数丢失或不正确,则不会调用 delegate
,并且错误将附加到 ArgumentParser
对象中包含的 string
消息中。例如,包含非数字字符的整数参数,或者未以 +
或 -
结尾的布尔参数。
客户端委托的参数验证
delegate
s 也有机会验证参数。如果需要提供消息,可以将其作为 string
返回。
private string ActiveDelegate (bool val)
{
this.active_ = val;
return "SSN set to " + val ? "active." : "inactive.";
}
如果 delegate
发现了错误条件,它可以抛出异常。ArgumentParser
对象会捕获该异常,并将消息附加到消息 string
中。
private string SSNDelegate (string val)
{
if (false == IsCorrectFormattedForSSN (val))
{
throw new ArgumentException ("Bad SSN format " + val);
}
this.ssn_ = val;
return "";
}
在实现客户端 delegate
s 之后,下一步是声明 Argument
数组。Argument
类存储用于匹配的令牌名称、客户端 delegate
,并提供基于数据类型的语法验证的内部方法。
public Argument[] GetArgumentDefinitions
{
get
{
Argument[] arguments =
{
new Argument ("ssn", new ArgumentString (this.SSNDelegate)),
new Argument ("active", new ArgumentBool (this.ActiveDelegate))
};
return arguments;
}
}
最后,客户端程序将所有内容组合在 Main
方法中。下面的代码片段被缩减了,只是为了展示关键部分。
class ArgTest
{
[STAThread]
static void Main(string[] args)
{
ArgTest a = new ArgTest ();
ArgumentParser p = new ArgumentParser (a.GetArgumentDefinitions, args, 0);
}
}
关注点
本文附带的源代码包含一个完整的测试程序,演示了每种 delegate
类型的用法,确定是否发现了错误,并将消息显示到控制台。项目中还包含一个帮助文档。
有关更多信息,请参阅 Command 目录中的 readme.txt 文件。
历史
- 2006年3月9日:首次发布