65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (17投票s)

2018 年 3 月 27 日

CPOL

7分钟阅读

viewsIcon

31984

downloadIcon

737

一个灵活的命令行解析类,它使用 C# 属性和反射来发现语法

引言

有时,回退到命令行仍然很有用,尤其是在应用程序可能通过脚本调用时。此库中的类提供了一种轻松获取命令行选项的方法,而无需担心解析命令行的复杂性。

要使用这些类,只需创建一个 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 布尔属性指定了 isRequiredposition。由于布尔选项要么存在要么不存在,并且没有关联的值,因此所请求的行为是不可能的。
DuplicatePropertyException 多个属性指定了相同的 name
InvalidOptionNameException 属性指定了一个无效的选项 name。选项名称必须至少有一个字符,以字母开头,并且完全由字母和数字组成。
IsRequiredExcludesException 属性同时指定了 isRequiredexcludes。这实际上会**始终**排除指定的属性。
NoPropertySetterException 属性缺少 public setter 方法。
PropertyPositionException 属性指定了一个 position,该位置会导致零基位置序列中出现间隙,或重复该序列中的另一个位置。
PropertyReferenceException 属性指定了 excludesrequires,其中选项名称标识了一个未定义的选项,被指定了多次,引用了自身,或者同时出现在 excludesrequires 中。

来自 CommandLineParseException 的完整异常集如下

类名 描述
CommandLineOptionException 与特定选项关联的所有 CommandLineParseException 异常的基类。
ConflictOptionException 提供的排除选项与排除它的选项一起出现。
DuplicateOptionException 同一个选项被指定了多次。
InvalidCommandLineArgumentException 指定了未识别的选项或意外的值。
InvalidOptionValueException 为选项指定了无效的值(例如,为整型属性提供了文本值)。
MissingOptionValueException 非布尔选项缺少其值。
MissingRequiredOptionException 缺少了必需的选项。

关注点

当一个值被指定为一个单独的参数时,它不能以斜杠字符(或 CommandLineParser.OptionIndicator)开头。默认情况下,当参数由 Windows 分割时,会删除引号。因此,很难区分“/option”和 /option。要做到这一点,需要解析原始命令行。

历史

  • 2018/3/27 - 上传了原始版本。
  • 2018/3/27 - 进行了小的错别字修复和外观更改。
© . All rights reserved.