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

控制台程序的通用操作系统参数

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2018年9月3日

BSD

10分钟阅读

viewsIcon

9179

downloadIcon

151

本文演示了一个支持默认值存储在应用程序设置中的命令行参数的类库。

引言

本文演示了一个类库,它实现为Microsoft .NET DLL,目标是Microsoft .NET Framework 4.6.1版本和C#编译器7.3版本。如果您拥有任何版本的Visual Studio 2017,并且您的安装是最新的,您应该拥有正确的框架和编译器。否则,项目将无法构建,因为它配置为需要正确的C#编译器版本。

背景

本文演示的类库以及随附的演示存档中包含的类库,代表了十多年前开始的工作的结晶,并且是许多尝试以我所能达到的“即插即用”的方式实现以下目标的最新尝试。

  1. 数据驱动:该库完全由数据驱动;参数在嵌入在参数适用的入口程序集中的文本资源中定义。
  2. 支持本地化:输出显示的显示名称可以定义为从入口程序集或对应于第二种用户界面语言的卫星程序集中读取的文本字符串。显示名称是可选的;省略的显示名称将被内部参数名称替换。
  3. 支持规则验证:验证规则和验证引擎已内置到类中。
  4. 可扩展的验证引擎:由于它是作为抽象方法实现的,因此如果需要样本中未涵盖的规则,则可以替换验证引擎。
  5. 实现为类库:将其实现为类库可以简化集成到新应用程序中;您只需要添加一个引用并创建定义参数的嵌入式文本文件资源。

使用代码

首先,设置对WizardWrx.Core.dllWizardWrx.OperatingParameterManager.dll的引用,并在Program.cs中添加相应的using语句。

using WizardWrx.Core;
using WizardWrx.OperatingParameterManager;

接下来,创建OperatingParametersCollection对象。

   OperatingParametersCollection<ParameterType , ParameterSource> operatingParametersColl = OperatingParametersCollection<ParameterType , ParameterSource>.GetTheSingleInstance (
     Properties.Settings.Default.Properties ,
     Properties.Resources.PARAMETER_DISPLAY_NAME_TEMPLATE ,
     ParameterSource.ApplicationSettings );

上面的语句需要一些解释。

  1. 由于OperatingParametersCollection类实现了单例设计模式,因此您必须调用其静态GetTheSingleInstance方法来获取唯一实例的引用。将OperatingParametersCollection创建为单例意味着同一程序集或其他程序集中定义的其他例程可以在不使您的应用程序充斥重复副本的情况下使用参数,更不用说节省初始化它们的巨大开销了。
  2. OperatingParametersCollection不仅是单例,而且它还是一个泛型,具有不止一个,而是两个泛型类型,它们都是Enums。C#编译器7.3版本是第一个支持枚举作为泛型约束的版本。
  3. Properties.Settings.Default.Properties是一个SettingsPropertyCollection,它使应用程序设置可供OperatingParametersCollection使用。使用此属性而不是Properties.Settings.DefaultSettingsPropertyCollection与程序集的默认命名空间解耦,以便该库可以支持任何应用程序的设置,但代价是单个配置属性,该库无法使用这些属性而不破坏其主要目标。
  4. Properties.Resources.PARAMETER_DISPLAY_NAME_TEMPLATE是我放入入口程序集资源字符串中的一个字符串,以及代表两个参数名称的字符串。(我省略了第三个以演示省略显示名称会发生什么。在实际使用中,我会为所有参数定义显示名称字符串。)
  5. ParameterSource.ApplicationSettingsParameterSource枚举的一个成员,我将其定义在OperatingParameter类源文件(位于库中)的命名空间范围内。此值与在应用程序设置中定义了默认值的参数一起存储,以指示值来自的来源。公用布尔属性HasDefaultValueInAppSettings设置为True,表示参数具有默认值。枚举的另一个成员CommandLine用于标记从命令行命名参数获取其值的参数。
  6. ParameterType枚举驱动IsValueValid中的switch块,该switch块实现了验证引擎。
  7. ParameterSourceParameterType枚举可以定义在任何地方,但是将定义在逻辑上和物理上都靠近IsValueValid方法在OperatingParameterBase上的实现,可以稍微简化其定义,同时保持代替两个泛型的两个枚举的定义在一起。

下一个要定义的对象是WizardWrx.Core.CmdLneArgsBasic对象,它定义如下。

    CmdLneArgsBasic cmdArgs = new CmdLneArgsBasic (
     operatingParametersColl.GetParameterNames ( ) ,
     CmdLneArgsBasic.ArgMatching.CaseInsensitive );

构造函数接受两个参数。

  1. 实例方法operatingParametersColl.GetParameterNames()返回一个字符串数组,每个字符串代表一个操作参数的内部名称。此列表被馈送到构造函数,构造函数使用它来初始化它用于解析命令行并按需返回参数值的集合。
  2. 枚举类型CmdLneArgsBasic.ArgMatching.CaseInsensitive指示CmdLneArgsBasic对象将参数视为不区分大小写。尽管OperatingParametersCollection对象区分大小写,但我通常遵循已建立的Windows命令行约定,即参数名称不区分大小写。
  3. 由于CmdLneArgsBasic构造函数会解析命令行,因此该对象已准备好立即返回参数值。
  4. 敏锐的读者会注意到,args数组明显不在传递给CmdLneArgsBasic构造函数的参数列表中。这是可能的,因为构造函数从System.Environment单例获取它们,该单例使它们可供控制台和图形模式程序使用。此功能使得编写接受命令行参数的Windows程序变得容易,我个人工具箱中有许多这样的程序。此构造函数设计的另一个好处是,可以从任何地方调用CmdLneArgsBasic构造函数,因为它会忽略args数组。

第三个准备步骤是设置那些没有默认值,或者其默认值已被命令行参数覆盖的操作参数。

    operatingParametersColl.SetFromCommandLineArguments (
     cmdArgs ,
     ParameterSource.CommandLine );

SetFromCommandLineArguments方法将CmdLneArgsBasic对象中存储的命令行参数集合馈送到一个例程中,该例程迭代集合,将名称与定义的参数匹配,并设置或更新其值。

  1. 通过命令行参数初始化或覆盖的操作参数值,都用ParameterSource枚举的CommandLine成员进行标记。
  2. 如果命令行参数提供的值覆盖了从应用程序配置设置的默认值,则通过将其复制到公共ParamSource属性来保留原始值。

第四个也是最后一个准备步骤是验证参数。

    foreach ( string strParamName in operatingParametersColl.GetParameterNames ( ) )
    {
     OperatingParameter<ParameterType , ParameterSource> operatingParameter = operatingParametersColl.GetParameterByName ( strParamName );
     bool fParamIsValid = operatingParameter.IsValueValid<ParameterType> ( );
     Console.WriteLine (
      Properties.Resources.MSG_VALIDATION_DETAIL ,            // Format Control String: Parameter {0} value of {1} is {2}.
      operatingParameter.DisplayName ,                        // Format Item 0: Parameter {0}
      Utl.RenderStringValue (
       operatingParameter.ParamValue ) ,                      // Format Item 1: value of {1}
      fParamIsValid );                                        // Format Item 2: is {2}
    }   // foreach ( string strParamName in operatingParametersColl.GetParameterNames ( ) )

一个简单的ForEach循环迭代由GetParameterNames返回的参数名称数组,然后通过调用GetParameterByName来获取每个OperatingParameter对象的引用。通过调用实例方法IsValueValid<ParameterType>()来验证参数值,该方法返回TrueFalse,并在返回True时将其ParamState属性设置为ParameterState.Validated

由于它适用于所有派生自它的对象,因此ParameterState枚举定义在属于该库的OperatingParameterBase类上。

尽管设置值的例程可以验证它,但我选择将这两个函数解耦,以便在验证任何参数之前可以加载和列出参数。分离验证使您可以选择在识别出第一个无效参数后停止,或者在停止之前完成评估。

严格来说,有第五个步骤,但它是设计时步骤,即定义参数并创建驱动整个过程的文本文件。

InternalName ParamType
表1列出了演示程序的参数。
OperatingParameter1 ExistingDirectory
OperatingParameter2 ExistingFile
OperatingParameter3 NewFile

 

 

 

 

该表构建为一个TAB分隔的文本文件,包含两列,一个标签行,以及每个参数的详细信息行。由于此演示程序定义了3个参数,因此其表包含3行。我通常在Microsoft Excel工作表中布局此类文件,当复制到Windows剪贴板时,Excel会方便地将其转换为制表符分隔的文本,然后将文本粘贴到Visual Studio文本编辑器窗口中以创建嵌入式资源。由于解析器知道如何处理字节顺序标记(BOM),因此Visual Studio文本编辑器添加的UTF-8 BOM不会造成任何干扰。

在使用文本文件之前,必须将其作为文本资源嵌入到程序集中;这通过将其Build Action设置为Embedded Resource来完成。构建引擎会处理其余的工作。

关注点

幕后发生的事情太多了,我必须提供一个详尽的列表,否则这篇文章会非常冗长乏味,或者我可以突出一些要点,并邀请您自己探索。

  • 主例程报告了大量信息,其中一些信息仅与演示偶然相关。但是,我保留了它,因为其中很多信息本身就很有趣。
  • 第一个可执行语句获取对ConsoleAppStateManager单例的引用,该单例公开了我用于快速组装新的字符模式程序的许多方法和属性。其中第一个是紧随其后的语句,它显示一个启动横幅,其中包含程序的名称和版本,以及当前的本地时间和UTC时间。
  • 第三个可执行语句设置一个枚举属性,该属性带有FlagsAttribute;您可能希望禁用ExceptionLogger.OutputOptions.EventLog,它会导致所有异常写入Windows应用程序事件日志。但是,在这样做之前请三思,因为这些异常报告曾多次挽救我免于痛苦,方法是确保即使我来不及记下飞逝的异常报告也能在事件日志中得到保存。
  • 主例程的大部分执行都包含在一个大的Try/Catch块中,该块确保所有异常都被正确捕获并记录在Windows事件日志和标准错误流中。
  • 第一块输出(Console.WriteLine)语句显示了一些有趣的附带信息。
    • 入口点例程的命名空间列出了定义入口点例程的类的命名空间,该例程恰好是当前正在执行的例程。收集此信息是开发此库的研究的一部分,尽管我找到了无需引用它即可实现目标的方法。
    • 导出类的程序集的入口点例程的命名空间明确表明,就Microsoft .NET Framework而言,类库没有入口点。
    • Utl.ListInternalResourceNames是一个小型库例程,用于列出执行程序集中定义的资源集合的名称。
    • AppSettingsForEntryAssembly是我创建的一个小类,用于包装应用程序设置,以简化在其他程序集中为它们设置引用。接下来的两个小块使用其方法间接检索值,通过库程序集。
    • 在演示开始之前调用的最后一个方法是appSettingsForEntryAssembly.ListAllAppSettings,它列出了所有设置。
  • 静态实用方法Utl.ListPropertiesPerDefaultToString是一种非常灵活的方式来列出类通过其自定义ToString方法报告的属性。我使用自定义ToString方法在局部变量和监视窗口中显示基本属性,而无需展开对象来显示其属性。
  • catch块使用级联的IF语句块,当异常消息以一组前缀之一开头时,设置状态码。

我对于幕后发生的事情比空间允许我涵盖的要多很多,这一点我并不道歉。但是,每个类、属性和方法都有XML帮助,这些帮助会显示在IntelliSense中,其中大部分都经过了细致的交叉引用。我还包含了每个自定义程序集的PDB文件,没有任何内容被混淆,并且在标准的3条款BSD许可下均可免费使用。在我发布这篇文章几周后,我终于获得了WizardWrx .NET API的源代码,其中包含此项目使用的大部分库的更新版本。昨天,我更新了库,加入了一些新方法和一个新的字符串常量。由于该库不受这些更改的影响,这些更改都是新代码,并且自首次发布以来我没有重新构建过该库,因此文章存档保持不变。

路线图

该库的路线图很明确。我预计未来版本将向ParameterType枚举添加类型,以便在改进的IsValueValid例程中支持其他规则。正在考虑的添加项包括对文件系统的其他验证以及对基本正则表达式的评估。

历史

2018年9月3日星期一是本版第一期的发布日期。

2018年10月8日星期一,我添加了指向WizardWrx .NET API源代码的链接。

© . All rights reserved.