通过属性实现灵活的 C# 命令行解析






4.84/5 (17投票s)
一个灵活的命令行解析类,
引言
有时,回退到命令行仍然很有用,尤其是在应用程序可能通过脚本调用时。此库中的类提供了一种轻松获取命令行选项的方法,而无需担心解析命令行的复杂性。
要使用这些类,只需创建一个 C# 类,为用户可以指定的每个选项定义 public
属性,并使用描述其命令行语法的属性来修饰每个属性。
然后,类库将通过反射发现语法,处理任何必要的类型转换,并强制执行有关必需或排除选项的任何规则。
包含的演示需要 Visual Studio 2017(或更高版本)才能生成。该解决方案最初是使用 Visual Studio Professional 构建的,并且包含一个单元测试项目。如果单元测试项目对您的 Visual Studio 版本造成问题,可以安全地将其删除。
背景
这是一个常见问题,市面上有很多其他包提供了类似(或更强大)的功能。这个特定的库设计得轻巧、灵活、经过充分测试,并满足我的特定需求。我将其分享给社区,以防其他人有类似的需求。
Using the Code
从消费者的角度来看,此库中只有这三个简单的类
类名 | 描述 |
CommandLineClassAttribute | 使用此属性来修饰选项类,以提供可选帮助资源的位置。 |
CommandLinePropertyAttribute | 使用此属性来修饰选项类中的属性,并为命令行的选项语法提供规则。 |
CommandLineParser | 此类提供了对指定了上述属性的选项类的解析器。 |
下面提供了一个选项类的简单示例
using Transeric.CommandLine;
using CommandLineParserDemo.Properties;
namespace CommandLineParserDemo
{
[CommandLineClass(typeof(Resources))]
public class SimpleOptions
{
[CommandLineProperty("optionA")]
public string OptionA { get; set; }
[CommandLineProperty("optionB", excludes:"optionA")]
public int OptionB { get; set; }
}
}
此类描述了一种语法,用户可以在命令行中指定 /optionA:ABC
或 /optionB:123
,但不能同时指定两者。解析器类会自动处理数据类型转换和排除规则的执行。
使用此选项类的程序可以像下面一样简单
using System;
using Transeric.CommandLine;
namespace CommandLineParserDemo
{
public class Program
{
public static void Main(string[] args)
{
var options = new SimpleOptions();
var parser = new CommandLineParser(options);
if (parser.Parse(args))
{
parser.WriteHelp(Console.Out);
return;
}
Console.WriteLine(options.OptionA);
Console.WriteLine(options.OptionB);
}
}
}
通过此,用户输入的命令的任何以下格式都将有效
.\CommandLineParserDemo.exe /optionA:ABC
.\CommandLineParserDemo.exe /optionA=ABC
.\CommandLineParserDemo.exe /optionA ABC
.\CommandLineParserDemo.exe /optionB:123
.\CommandLineParserDemo.exe /optionB=123
.\CommandLineParserDemo.exe /optionB 123
以下格式无效。在第一种情况下,值“ABC
”对整数属性 SimpleOptions.OptionB
无效。在第二种情况下,“optionB
”关联的 excludes
参数阻止了“optionA
”也被指定。
.\CommandLineParserDemo.exe /optionB:ABC
.\CommandLineParserDemo.exe /optionA=ABC /optionB:123
虽然在上面的示例中不明显,但解析器还会自动识别缩写。用户只需提供足够的字符,以便选项名称明确无误。例如,如果语法中有两个不同的选项 /alpha
和 /beta
,用户只需指定 /a
和 /b
。
对于 WinForm 和 WPF 应用程序,命令行参数可能不那么容易获得。最简单的方法是通过 Environment.GetCommandLineArguments
方法或 Environment.CommandLine
属性来获取它们。
本文的其余部分提供了可能超出普通用户需求的详细技术信息。直接试用本文提供的示例应用程序可能会更快。示例应用程序分为三个项目
项目名称 | 描述 |
CommandLineParserDemo | 一个简单的应用程序,通过显示请求的文件名来演示此类库。 |
Transeric.CommandLine | 解析命令行所需的所有代码。这是唯一需要在您自己的应用程序中包含的类库。 |
Transeric.CommandLine.Tests | Transeric.CommandLine 项目的所有单元测试。这对您自己的应用程序来说**不是**必需的。 |
CommandLineClassAttribute 类
如前所述,CommandLineClassAttribute
用于修饰选项类以提供帮助资源的位置。其参数如下
参数名称 | 描述 |
resourceType | 这提供了项目中资源类的一种类型。在 Visual Studio 中,可以通过使用“添加”>“新建项…”>“资源文件”轻松地将这些类添加到项目中。 |
helpKey | 这提供了包含帮助文本的 string 资源的名称。如果省略,则假定一个默认值。默认帮助键是从类名生成的,后面加上文本“_Help ”。因此,例如,类 MyOptions 的默认帮助键将是“MyOptions_Help ”。 |
CommandLinePropertyAttribute 类
如前所述,CommandLinePropertyAttribute
用于修饰选项类中的属性,以提供选项的命令行语法和帮助资源的位置。其参数如下
参数名称 | 描述 |
名称 | 在命令行上出现的选项的必需名称。 |
helpKey | 这提供了包含帮助文本的 string 资源的名称。如果省略,则假定一个默认值。默认帮助键是从属性名生成的,后面加上文本“_Help ”。因此,例如,属性 MyProperty 的默认帮助键将是“MyProperty_Help ”。 |
requires | 提供了一个可选的逗号分隔的选项名称列表,这些选项必须在指定此选项时同时存在。 |
excludes | 提供了一个可选的逗号分隔的选项名称列表,这些选项在指定此选项时被禁止。 |
位置 | 虽然所有选项都可以按名称指定(例如,命令 /optionA:ABC ),但某些选项也可以按位置指定(例如,命令 ABC)。此参数提供了此类选项的零基索引。在提供的示例中,position 将为零(不计算命令)。如果该位置会导致位置序列出现间隙或重复,则会发生异常。 |
isRequired | 这提供了一个布尔值,指示该选项是否始终必需。 |
listSeparatorChar | 某些选项可能与具有列表或数组数据类型的属性相关联。此参数提供了用于在命令行上分隔列表项的字符。默认值为逗号(',')。因此,例如,以下选项可能是有效的:/list:option-1,option-2,option-3 。 |
异常处理
此类库可能抛出大量异常。但是,几乎所有这些异常都源自三个类之一
类名 | 描述 |
CommandLineClassException | 可能从 CommandLineParser 的构造函数中抛出这些异常,以指示 CommandLineClassAttribute 附加到选项类时存在问题。 |
CommandLinePropertyException | 可能从 CommandLineParser 的构造函数中抛出这些异常,以指示 CommandLinePropertyAttribute 附加到选项类中的属性时存在问题。 |
CommandLineParseException | 可能由 CommandLineParser.Parse 方法抛出这些异常,以指示语法错误或数据类型转换错误。 |
来自 CommandLinePropertyException
的完整异常集如下
类名 | 描述 |
BooleanRequiredOrPositionalException | 布尔属性指定了 isRequired 或 position 。由于布尔选项要么存在要么不存在,并且没有关联的值,因此所请求的行为是不可能的。 |
DuplicatePropertyException | 多个属性指定了相同的 name 。 |
InvalidOptionNameException | 属性指定了一个无效的选项 name 。选项名称必须至少有一个字符,以字母开头,并且完全由字母和数字组成。 |
IsRequiredExcludesException | 属性同时指定了 isRequired 和 excludes 。这实际上会**始终**排除指定的属性。 |
NoPropertySetterException | 属性缺少 public setter 方法。 |
PropertyPositionException | 属性指定了一个 position ,该位置会导致零基位置序列中出现间隙,或重复该序列中的另一个位置。 |
PropertyReferenceException | 属性指定了 excludes 或 requires ,其中选项名称标识了一个未定义的选项,被指定了多次,引用了自身,或者同时出现在 excludes 和 requires 中。 |
来自 CommandLineParseException
的完整异常集如下
类名 | 描述 |
CommandLineOptionException | 与特定选项关联的所有 CommandLineParseException 异常的基类。 |
ConflictOptionException | 提供的排除选项与排除它的选项一起出现。 |
DuplicateOptionException | 同一个选项被指定了多次。 |
InvalidCommandLineArgumentException | 指定了未识别的选项或意外的值。 |
InvalidOptionValueException | 为选项指定了无效的值(例如,为整型属性提供了文本值)。 |
MissingOptionValueException | 非布尔选项缺少其值。 |
MissingRequiredOptionException | 缺少了必需的选项。 |
关注点
当一个值被指定为一个单独的参数时,它不能以斜杠字符(或 CommandLineParser.OptionIndicator
)开头。默认情况下,当参数由 Windows 分割时,会删除引号。因此,很难区分“/option
”和 /option
。要做到这一点,需要解析原始命令行。
历史
- 2018/3/27 - 上传了原始版本。
- 2018/3/27 - 进行了小的错别字修复和外观更改。