C#怎么解析命令行参数 System.CommandLine库高级用法

System.CommandLine 的核心优势是将命令行输入强类型映射为 C# 对象,支持 Option、Argument、子命令嵌套、自定义解析与验证,并通过泛型 Handler 实现零反射调用。

用 System.CommandLine 定义强类型命令行参数

System.CommandLine 的核心优势是把命令行输入直接映射为 C# 对象,避免手动拆解 args 字符串。关键不是“怎么加参数”,而是“怎么让参数自动变成你想要的类型和结构”。

比如定义一个发布命令:myapp publish --project MyApp.csproj --configuration Release --output ./bin,你可以这样建模:

  • Option 表示单值参数(如 --project),指定别名(-p)、描述、是否必需
  • Option> 支持多次出现的参数(如 --property Key=Value 可重复)
  • Argument 处理位置参数(如 myapp run api 中的 api
  • 所有选项/参数都可绑定到 Handler 方法的参数上,System.CommandLine 自动完成转换和验证

用 Handler 和泛型委托实现零反射调用

老式做法常依赖反射调用方法,而 System.CommandLine 推荐用强类型委托,既安全又高效。不用写 typeof(...).GetMethod(...).Invoke() 这类易错代码。

例如:

var publishCommand = new Command("publish", "Publish a .NET project");
publishCommand.AddOption(projectOption);
publishCommand.AddOption(configOption);
publishCommand.AddOption(outputOption);

publishCommand.Handler = CommandHandler.Create(PublishHandler);

// 方法签名完全匹配,自动注入解析后的值 static void PublishHandler(string project, string configuration, string output) { Console.WriteLine($"Publishing {project} in {configuration} to {output}"); }

注意:参数名必须和 Option.Argument.GetDefaultValue() 或绑定名一致;如果想用不同名,可用 Option.AliasOption.SetName() 显式对齐。

支持子命令 + 共享选项 + 配置复用

真实 CLI 工具往往有层级,比如 git commitgit push。System.CommandLine 原生支持子命令嵌套,还能让多个子命令共用一组基础选项(如 --verbose--dry-run)。

  • RootCommand.AddCommand(subCmd) 构建树形结构
  • 共享选项不绑定到具体命令,而是添加到 RootCommand,再通过 InvocationContext.ParseResult.GetValueForOption() 在各 handler 中按需读取
  • 也可用 Command.AddGlobalOption()(.NET 7+)自动下推到所有子命令
  • 配合 ParseResult.FindResultFor() 可判断某个选项是否被显式传入(区别于默认值)

自定义解析逻辑与错误处理更可控

当标准类型转换不够用(比如要解析自定义时间格式、枚举别名、文件路径存在性校验),System.CommandLine 允许你插入自己的转换器和验证器。

  • option.ParseArgument = result => { /* 自定义逻辑 */ } 替换默认解析
  • option.AddValidator(opt => { if (...) return "Invalid format"; }) 提供友好错误提示
  • 全局异常处理可通过 CommandLineBuilder.UseExceptionHandler() 捕获 CommandException 等,并输出结构化帮助或退出码
  • 错误消息不会直接抛出堆栈,而是走内置渲染器,保持 CLI 体验统一

基本上就这些。System.CommandLine 的高级用法,核心是“声明即契约”——你定义的 Option、Argument、Handler 就是用户能输入什么、程序会收到什么、出错时反馈什么。不复杂但容易忽略的是:别急着写逻辑,先花十分钟把命令结构用对象模型画清楚。