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

一个基于 C#/.NET 属性的命令行参数解析器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (9投票s)

2012 年 1 月 10 日

CPOL

21分钟阅读

viewsIcon

45166

downloadIcon

1647

本文介绍了一个易于使用的基于属性/反射的库,用于无缝解析应用程序的命令行参数。

引言

任何非简单的程序通常都会为用户提供一些选项。对于控制台应用程序,这些选项通常由命令行参数指定,这些参数作为 `string` 数组传递给 C# 应用程序的 `Main` 方法,例如:

static void Main(string[] args) { }

这些参数需要解析。这可以通过手动编写自定义实现来完成,但这容易出错,并且在为几个应用程序实现后变得乏味。许多程序员都面临过这个问题,并导致了各种库的出现,例如 C 和 UNIX 的 getopt,以及类似的 Java 和 C# 移植。getopt 和其他方法的问题在于它们只自动化了部分过程。它们仍然要求开发人员有时实现一个通常包含大型 `switch` 语句的处理循环,并且在出现错误时生成使用 `string` 以及各种其他设置过程。

本文展示了一种使用 NAttrArgs 库的 C# 不同方法。这消除了开发人员实现任何解析代码的需要。它还将处理命令行参数的编程模型从命令式更改为声明式。这意味着开发人员需要做的就是指定哪些成员变量、成员属性和/或成员函数应该响应命令行参数的存在而被设置。

从命令式到声明式风格的转变类似于 LINQ 引入的转变,其中查询从关注实现(命令式)变为只声明查询应该做什么(声明式)。LINQ 使用额外的 C# 编译器语法糖使开发人员的过程非常简单,而 `NAttrArgs` 库顾名思义依赖于 .NET 属性。

`NAttrArgs` 库、示例以及库的二进制文件的所有源代码都作为附带的 zip 文件提供。源代码是开源的,可在 GitHub 上获取:https://github.com/petebarber/NAttrArgs

Using the Code

了解 `NAttrArgs` 如何工作的最简单方法是从一个简单的示例开始。

class Program
{
    [NArg]
    private string _testArg;

    [NArg(IsOptional = true, AltName = "foo")]
    private bool _optionalFlag;

    static void Main(string[] args)
    {
        try
        {
            new Program().Run(args);
        }
        catch (NArgException e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }

    void Run(string[] args)
    {
        new ArgParser<Program>("Example2").Parse(this, args);

        Console.WriteLine("_TestArg:{0}", _testArg);
        Console.WriteLine("_optionalFlag:{0}", _optionalFlag);
    }
}

这展示了一个简单的程序,它有一个必需参数和一个可选标志参数。无需添加任何特殊的解析代码,最终结果应该是如果这些命令行参数存在,那么用 `NArg` 属性修饰的变量应该被适当地设置。

不带任何参数运行会生成以下输出:

Usage: Example2 [-foo] <_testArg>

这是因为程序期望为 `_testArg` 提供一个值。这通过一个空的 `NArgAttribute` 声明。默认情况下,参数被认为是强制性的,除非属性参数 `IsOptional` 设置为 `true`。这在第二个变量 `_optionalFlag` 的声明中可以看到。

有两种类型的可选参数,首先是布尔标志参数,它表示一个可以打开或关闭的设置,例如“`somecmd -recurse`”。其次是一个接受参数的可选参数,例如“`somecmd -bgcolour red`”。在这些情况下,如果未指定选项,则变量的值将保持不变。在上面的示例中,如果指定了“`-foo`”,则 `_optionalFlag` 将设置为 `true`。

由于没有指定命令行参数,因此对 `Parse()` 的调用抛出了一个异常,因为它期望至少找到一个可用于设置 `_testArg` 值的参数。用法消息由 `NAttrArgs` 构建,并存储在抛出的 `NArgException` 的 `Message` 属性中。

用法消息的格式是指定给 `ArgParser` 构造函数的程序名称,后跟可选参数(如果有),然后是强制参数(如果有),最后是一个省略号,表示已指定一个变量(类型为 `string[]`)以接收任何未使用的参数;在此示例中未指定。

用法消息中每个参数的名称默认是关联变量的名称。正如所见,名称 `_testArg` 不是很友好。可以通过 `NArg` 属性的 `AltName` 选项为参数关联不同的名称。这用于在 `_optionalFlag` 的情况下显示“`foo`”。

如果改为使用命令行参数“`-foo`”和“`bar`”运行示例程序,它将显示:

_TestArg:bar
_optionalFlag:True

发生的情况是 `_testArg` 变量已设置为值“`bar`”。由于变量是 `string` 类型,因此从命令行参数 `string` 到 `string` 变量的映射很简单。在 `_optionalFlag` 的情况下,没有实际的命令行参数值可设置。相反,选项的存在导致 `NAttrArg` 解析代码将此变量的值设置为 `true`。

接收变量的类型不仅限于 `string` 和 `bool` 的明显情况。它们可以设置为 .NET `Convert.ChangeType` 方法可以处理的任何类型。此外,允许从 `char` 到 `bool` 的转换;但这仅在可选标志的变量类型为 `char` 且内部代码尝试将其设置为 `true` 时在内部使用。此外,如果目标类型(变量的类型)可通过 `Type.IsAssignable` 从源类型赋值,则也会使用此方法进行类型转换。这主要也是为了内部使用,用于处理剩余参数存储为 `string[]` 的特殊情况,并且不是赋值给此类型的变量,而是赋值给 `IEnumerable` 类型的属性。

如果无法进行转换,例如变量类型为 `double`,而命令行参数(始终是 `string`)的值是“`bar`”而不是“`2.37`”,则这将导致 `Convert.ChangeType` 引发异常。这将在 `ArgParse.Parse` 方法中捕获,然后将抛出 `NArgAttrException`。与以前一样,用法消息将包含在 `Message` 属性中,但此外,原始异常将从 `InnerException` 属性中获得。

这可能看起来是处理这种情况的一种不优雅且不完整的方式。相反,它减轻了开发人员处理此问题的负担。通过指定接收值的变量类型,存在一个隐式契约,即命令行参数 `string` 值必须是可转换的。如果不是,那么这是一个用法错误。用法消息确实指定了可接受的值。对于任何数字,考虑到无限范围,这将很难。可能可以指定可接受的格式。然而,根据设计和约定,用法消息通常不传达此信息。相反,它留给了应用程序文档。

然而,`NAttrArgs` 有一个选项可以在转换前捕获不正确的值并在用法消息中显示可接受的值。这通过使用 `NArg` 属性的 `AllowedValues` 参数来实现。这接受一个包含可接受值的 `string` 数组。这些值在任何类型转换之前进行检查。下面的示例是前一个示例的修改版本。

class Program
{
    [NArg(AltName = "Test", AllowedValues = new string[] { "one", "two", "three" })]
    public string _testArg;

    [NArg(IsOptional = true, AltName = "foo", 
    AllowedValues = new string[] { "true", "false" })]
    public bool _optionalFlag;

    static void Main(string[] args)
    {
        try
        {
            new Program().Run(args);
        }
        catch (NArgException e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }

    void Run(string[] args)
    {
        new ArgParser<Program>("Example3").Parse(this, args);

        Console.WriteLine("_TestArg:{0}", _testArg);
        Console.WriteLine("_optionalFlag:{0}", _optionalFlag);
    }
}

现在强制性参数被限制为接受“one”、“two”或“three”。稍微有趣的情况是,布尔变量 `_optionalFlag` 现在被限制为“`true`”或“`false`”;这很有道理,因为它是布尔值。但是,指定 `AllowedValues` 参数将其从可选标志更改为接受参数的选项。用法消息显示了这一点:

Usage: Example3 [-foo <true|false>] <one|two|three>

后者修改毫无意义,因为指定“`-foo true`”与之前只使用“`-foo`”相同。因此,限制值的好处取决于目标变量的类型。当值受约束时,用法消息也会改变。不使用变量名称或替代名称,而是显示允许值的列表,每个值都由管道符号(“`|`”)分隔,表示“或”,源自 UNIX 正则表达式。

到目前为止的示例仅涉及单个可选和强制参数。可选参数后跟强制参数存在隐式排序。如果每种参数类型不止一个且排序很重要,则应使用 `NArg` 属性的 `Rank` 参数。如下所示,其中有三个强制参数和三个可选参数。

class Program
{
    [NArg(Rank = 3)]private string _mandatoryArg;
    [NArg(Rank = 2)]private string _mandatoryArg1;
    [NArg(Rank = 1)]private string _mandatoryArg2;

    [NArg(IsOptional = true, AltName = "alt1", Rank = 2)] private int _optionalArg;
    [NArg(IsOptional = true, AltName = "alt2")] private int _optionalArg1;
    [NArg(IsOptional = true, AltName = "alt3", Rank = 1)] private int _optionalArg2;

    static void Main(string[] args)
    {
        try
        {
            new Program().Run(args);
        }
        catch (NArgException e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }

    void Run(string[] args)
    {
        new ArgParser<Program>("Example4").Parse(this, args);

        Console.WriteLine("Arg0:{0}", _mandatoryArg);
        Console.WriteLine("Arg1:{0}", _mandatoryArg1);
        Console.WriteLine("Arg2:{0}", _mandatoryArg2);
        Console.WriteLine("Opt0:{0}", _optionalArg);
        Console.WriteLine("Opt1:{0}", _optionalArg1);
        Console.WriteLine("opt2:{0}", _optionalArg2);
    }
}

如果没有指定等级,但存在多个强制或多个可选参数,则解析顺序将与(用 `NArg` 属性)修饰的成员的定义顺序相对应。默认情况下,`Rank` 的值为 `0`,因此所有参数都相等,因此定义顺序优先。

相应的用法消息是:

Usage: Example4 [-alt2] [-alt3] [-alt1] <_mandatoryArg2> <_mandatoryArg1> <_mandatoryArg>

定义顺序已被 `Rank` 参数取代。请注意 `_optionalArg1` 没有给出排名,这意味着它保留了 `0` 的最高排名。要更改此变量的位置,需要指定较低的排名或在其之前定义另一个修饰变量。

不只是变量可以被修饰

到目前为止,只有成员变量被属性修饰。不仅显式成员变量可以设置其值。只要属性有 setter,就可以指定属性,方法也可以。为此,它必须没有参数,或者只有一个参数,该参数的类型是命令行参数可以转换的类型。没有参数可能看起来有点奇怪,但对于可选标志参数来说,它是一个完美的映射,因为调用意味着参数存在。尽管没有多大意义,但没有什么能阻止使用带有可选或强制参数的 `void` 方法。

调用 setter,特别是方法的能力,可以实现命令行参数与最终设置之间更复杂的映射。下面的示例演示将变量设置为从命令行获得的值,并乘以可选的命令行值。

class Program
{
    private uint _multiple;
    private double _multipleOfArgument;

    [NArg]
    public void Multiply(double value)
    {
        _multipleOfArgument = value * Multiple;
    }

    [NArg(IsOptional = true, OptionalArgName = "Multiple")]
    public uint Multiple
    {
        get { return Math.Max(1, _multiple); }
        private set { _multiple = value; }
    }

    static void Main(string[] args)
    {
        try
        {
            new Program().Run(args);
        }
        catch (NArgException e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }

    void Run(string[] args)
    {
        new ArgParser<Program>("Example5").Parse(this, args);

        Console.WriteLine("_multipleOfArgument:{0}", _multipleOfArgument);
    }
}

此示例还展示了 `NArg` 属性 `OptionalArgName` 参数的使用。`Multiple` 属性不是布尔值,需要用 `uint` 设置,因此可选参数需要一个参数。此参数的名称(在构建用法消息时使用)由 `OptionalArgName` 参数指定。名称中的“可选”表示它是可选参数的参数,而不是它本身是可选的。

同一个方法不能用于不同的命令行参数。但是,如果用于可选参数,则没有任何限制重复调用。如果与不同的(命令行)值一起使用,则可以创建一个累积值,例如“`somecmd -add 1 -add 2 -add 3`”,这可以累积每个值,从而导致一个变量存储“`6`”。

能够通过属性指定方法,避免了创建临时成员变量来存储获取的值,然后再在方法中对其进行处理的麻烦。可以想象,可以将整个程序逻辑放在这些方法之一中,或从那里启动操作。如果方法是一个函数(有返回值而不是 `void`),则忽略它。

任何剩余的命令行参数会发生什么?

默认情况下,任何无法识别的可选参数都会生成错误,任何剩余的非可选参数也会生成错误,尽管 `InnerException` 会有所不同。这种情况很好,除非应用程序需要一个无限制的最终参数列表来处理,例如一组要列出其信息的或转储其内容的 文件。这就是使用 `IsConsumeRemaining` 的最终 `NArg` 属性参数的地方。

class Program
{
    [NArg(IsOptional = true, AltName = "Count")] 
    public bool IsShowCount { get; private set; }
    [NArg(IsConsumeRemaining = true)] private string[] _theRest;

    private static void Main(string[] args)
    {
        try
        {
            new Program().Run(args);
        }
        catch (NArgException e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }

    private void Run(string[] args)
    {
        new ArgParser<Program>("Example6").Parse(this, args);

        if (IsShowCount == true)
            Console.WriteLine("There are {0} remaining arguments", _theRest.Length);

        foreach (string s in _theRest)
            Console.WriteLine(s);
    }
}

任何剩余的参数现在都转换为 `string` 数组,并且可以像任何其他带有属性的成员一样传递给变量、属性或方法。目标类型必须可从 `string[]` 转换。特别是,如果 `string` 都是整数,并且目标类型是 `int[]`,那么即使从 `string` 到 `int` 的转换在这种情况下是合法的,数组类型之间的转换也不是,即转换不是按元素执行的。为将剩余的 `string` 值分配给 `IEnumerable` 提供了显式支持。提供了一个示例 6 的替代实现,演示了这一点。

如果多个成员用 `NArg(IsConsumeRemaining = true)` 修饰,则使用第一个,其余的将保持不变。用此参数修饰的成员本质上是一个可选参数,因为如果没有剩余参数,它不会导致错误。Rank 无关紧要,因为根据定义,只有在所有其他命令行值都被消耗后才使用。如果这些参数中的任何一个被设置,它们将被忽略。`AllowedValues` 也是如此。剩余的值原封不动地传递给被修饰以接收它们的成员。成员的名称也无关紧要,因为在用法消息中,剩余的参数被表示为“`[...]`”。

NArgAttribute 用法

下面是 `NArg` 属性不同属性的摘要。并非所有属性都可以一起使用,但如果发生这种情况,则不会出现错误。相反,解析机制将推断出最合适的用法。如果属性在属性用法中重复,则最后一次使用优先。

IsOptional

语法

NArg(IsOptional = true | false) 

备注

默认值为 `false`,这意味着任何用 `NArg` 属性修饰的成员都被视为强制性的。

语法

NArg(Rank = <uint>) 

备注

默认值为 `0`。解析时,可选参数在强制参数之前考虑。这意味着可选参数和强制参数形成两组参数,每组都有自己的排序,因此相同的序数可以用于强制参数和可选参数而不会冲突。可选参数在解析期间首先考虑。

当遇到第一个不以“-”为前缀的命令行参数时,可选参数解析结束。如果其他命令行参数确实以“`-`”开头,则它们不被视为此类。

AltName

语法

NArg(AltName = <string>) 

备注

默认情况下,用于生成用法消息的名称是修饰成员的名称。此属性允许将显示名称更改为指定的 `string`。

OptionalArgName

语法

NArg(OptionalArgName = <string>) 

备注

此属性仅适用于可选参数,即 `IsOptional = true` 的情况。设置此值会将可选类型从可选标志更改为可选参数。这现在要求命令行指定选项和附加参数,例如 `foo -count 8`。参数的值将用于设置修饰成员,而不是 `true` 或 `false`。

AllowedValues

语法

NArg(AllowedValues = <string[]>) 

备注

此属性允许限制修饰成员的值集。这些值以 `string` 形式指定,而不是成员的目标类型(如果不同)。

用法消息也会受到影响:不显示强制参数的名称或可选参数的名称,而是显示由“`|`”符号分隔的允许值列表。

如果除了 `IsOptional = true` 之外还设置了此属性,但未指定 `OptionalArgName`,那么由于允许值不再是布尔值,可选标志参数将隐式提升为带有参数的可选参数,并采用指定的值。即使允许值仅为“`True`”和“`False`”,这也是 `true`。

IsConsumeRemaining

语法

NArg(IsConsumeRemaining = true | false) 

备注

默认情况下,如果在所有可选和强制参数都匹配后仍有任何剩余的命令行参数,则这被视为错误。

在用 `NArg` 属性修饰的成员上设置此属性将不再意味着未处理的命令行参数构成错误。相反,它们将被分配给修饰的成员。这必须是 `string[]` 或 `IEnumerable` 类型。如果没有未处理的参数,则将分配一个零长度的 `string` 数组,即目标成员不会是 `null`。不进行其他类型转换。

只有单个成员被 `NArg` 属性修饰且设置了此属性才有意义。如果出现多次,则使用第一个。在与此属性结合使用时,其他 `NArg` 属性不适用。如果指定,它们将被忽略。这包括 `AllowedValues`。

您不限于控制台应用程序

虽然 `NAttrArgs` 库的主要应用是处理控制台应用程序的命令行参数,但这并不是唯一的用途。GUI 应用程序接受命令行参数是很正常的,尽管此功能不经常使用。以下代码摘录自示例 7,其中显示了 `NAttrArgs` 被用于处理传递给 WPF 应用程序的命令行参数,并且修饰的成员变量在 XAML 中使用。

注意:当通过 Environment.GetCommandLineArguments() 获取命令行参数时,第一个参数包含可执行文件的路径,而传递给 C# 控制台应用程序的参数则不包含。

App.xaml.cs

public partial class App : Application
{
    [NArg(IsOptional = true, AltName = "Show", OptionalArgName = "ShowMe")]
    public string ShowMe { get; private set; }

    App()
    {
        ShowMe = "Default boring message";
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        try
        {
            string[] cmdLineArgsIncAppName = Environment.GetCommandLineArgs();
            string[] cmdLineArgsOnly = cmdLineArgsIncAppName.Skip(1).ToArray();

            new ArgParser<App>("App").Parse(this, cmdLineArgsOnly);
        }
        catch (Exception)
        {
            Shutdown();
        }
    }
}

MainWindow.xaml

<Window x:Class="Example7_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Text="{Binding Path=ShowMe, 
        Source={x:Static Application.Current}, Mode=OneWay}" />
    </Grid>
</Window>

测试驱动开发(TDD)

这个库是我第一次尝试使用 测试驱动开发。所有测试都包含在 `NAttrArgs.Test` 项目中。测试是按类实现的,每个 `NAttrArgs` 项目中的类都对应一个 _*Test.cs_ 文件,有点!这些测试使用 NUnit 实现。

代码已经演变得非常细粒度,包含许多易于独立测试的小类。代码最初并非如此。最初它是一个单一类,然后是许多小类,然后又回到一两个大类,最终达到现在的状态。因此,相当多的测试,特别是那些测试参数解析和用法消息的测试,级别太高,应该被视为集成测试或验收测试。当前相当细粒度的解析代码实现现在允许它们作为一套完整的单元测试,按类实现。

进行 TDD 是一次有趣的经历。起初感觉像是在由内而外地编程,但我越习惯它,就越容易。拥有一套单元测试(和验收测试)允许代码持续重构;如前所述。发生的重构在提取类和类中的方法方面是深远的。能够频繁运行测试;有 152 个测试,它们在一两秒内运行;这意味着检查重构是否成功是微不足道的。

除了使用 TDD,还编写了一个测试程序来使用 `NAttrArgs`。这是一个简化版的 UNIX ls 命令。这也是使用 TDD 编写的,这对于第二个 TDD 项目来说有点错误。原因在于它需要 模拟 Windows 文件系统;尽管我更喜欢通用术语 测试替身。事实上,使用了前面链接中提到的假对象。为此,使用了 Microsoft Moles。这是一个隔离框架,具有有用的属性,即可以为密封类、静态类以及具有静态和非虚拟方法的类创建测试替身。对于 .NET 文件系统类,即 `File`、`FileSystemInfo`、`FileInfo` 和 `DirectoryInfo`,情况确实如此。

为了实现这一点,Moles 使用了 detour,这似乎是通过 .NET 分析 API 拦截方法调用并在运行时重写这些方法。这种额外功能的缺点是单元测试的运行时间比基于 NUnit 的测试长许多数量级。为了与 Visual Studio 集成,ls 示例的测试是使用 MSTest 而不是 NUnit 编写的。这是因为当使用 Moles 时,需要使用与 Moles 关联的 Pex 测试运行器(来设置分析 API)。MS Test 能够在 Visual Studio 中启动它,只要每个测试都用 Moles 的自定义属性装饰:`[HostType("Moles")]`。

TDD 红-绿-重构过程的一个部分比通常情况下进行了更进一步的重构阶段。最近观看了 Uncle Bob 的 Clean Coder 视频系列,使用了 'extract till to you drop' 习语。这导致方法大约有四行长,并且还有更多的类和方法。通过提取使类演化到它们现在包含许多方法的程度,内聚性 常常降低。然而,通常情况下,方法子集非常内聚,这意味着可以提取一个单独的类。这是可取的,并导致高度内聚但松散耦合的代码。

包里有什么?

`NAttrArgs` 解决方案主要有三个方面。首先是 `NAttrArgs` 项目本身。这是 `NAttrArgs` 库的源代码,构建后会生成 *NAttrArgs.dll* 程序集。其次是 `NAttrArgs.Test` 项目,它是 `NAttrArgs` 的基于 NUnit 的测试。最后是 Samples 解决方案目录,其中包含 ls 项目中的示例 ls 实用程序以及 `ls.TestMS` 项目中附带的 MS Test/Moles 单元测试。由于这并没有展示 `NAttrArgs` 的许多功能,因此还有一个最终项目,名为 `Test`。这个项目是一个简单的程序,展示了 `NAttrArgs` 的所有功能。

注意:与 NUnit 二进制文件不同,未提供 Moles 二进制文件。这是由于微软许可证只允许非商业和学术用户免费使用。如果需要运行 `ls.TestMS` 项目中的测试,请从微软适当获取副本。此解决方案不是包的关键部分,仅保留在此处,因为其他人可能会觉得看到如何为 .NET 文件系统类创建测试替身很有趣。

代码

鉴于 TDD 的使用(包括测试的存在)以及小而希望命名良好的方法的使用,代码应该是不言自明的。话虽如此,主要查看的两个地方是 *ArgParser.cs* 和 *NArgAttribute.cs*。后者是用于修饰类成员的自定义属性的定义。`ArgParser` 的构造函数通过使用反射获取所有这些被装饰的类成员来启动它们的使用。创建了三个列表(准确地说是 LINQ 查询),表示如果存在的话,必需、可选和剩余的被装饰成员。这些查询不返回 `NArgAttribute` 的实例,而是返回派生类 `MemberAttribute`。这主要增加了一个对被 `NArgAttribute` 装饰的类成员的引用。

解析命令行参数的工作按顺序分配给可选、必需和强制解析器,这些解析器包含在名称相似的文件中。它们使用 `ArgIterator` 类的一个实例。该类实现了 `IEnumerator`,并提供对命令行参数的枚举访问。之所以采用自定义实现而不是简单地从 `string[]` 获取实例,是因为它提供了允许将当前项推回的额外行为。可选参数解析器在遇到第一个必需选项时会使用此功能,因此需要将该参数推回,以便 `RequiredArgumentsParser` 消耗。

其他主要的感兴趣类是 `MemberSetter` 和 `CustomConvert`。当解析器为其当前参数找到匹配的成员时,`MemberSetter` 用于设置值。根据成员的类型,这将是设置变量或属性上的值,或调用方法。通过将 `NArgAttribute` 和 `MemberInfo` 信息组合到 `MemberAttribute` 中,这可以传递给 `MemberSetter`,它无论成员是可选参数还是必需参数,都可以通用地处理该成员。此类的入口点适用于可选参数、必需参数和剩余参数,但这些由通用实现处理,该实现使用 `MemberAttribute` 中的 `MemberInfo` 来确定如何访问成员变量、属性或方法。

设置值时,`CustomConvert` 类处理命令行参数的 `string` 类型与目标类型之间的任何必要转换。这主要通过 .NET 方法 `Convert.ChangeType` 执行,但该类处理从 `bool` 到 `char` 的特殊情况,这仅在可选标志的目标类型为 `char` 时才需要。另一个不那么特殊的情况是当目标类型可从源类型分配时。添加此功能是为了将用于处理剩余参数的 `string[]` 应用于 `IEnumerable` 目标类型,因为此类型不可转换。但是,对于其他可直接分配的类型,可以避免转换。

What Next?

这是 `NAttrArgs` 的 V1 版本。立即想到的一些额外功能包括:

  • 扩展用法消息

    除了显示生成的用法消息外,这将允许提供可选的更长的参数描述,该描述将显示在用法消息下方。

  • 允许将单个字母的可选标志组合成一个字符串

    例如,-abc 而不是 -a -b -c

  • 允许在选项后面不加空格或使用等号直接指定可选参数的值

    例如,-foobar 而不是 -foo bar 或 -foo=bar

  • 重构单元测试以完全按类进行
  • 为二进制文件创建 nuget

如果有什么吸引您或者有其他有用的东西,请告诉我。或者,您可以从 GitHub 分叉 源代码,如果您喜欢,可以贡献任何更改。

© . All rights reserved.