用 System.CommandLine 构建工程级 CLI 工具
用 System.CommandLine 构建工程级 CLI 工具
—— 从“手写 Command 模式”到工业级命令行框架
在实际工程中,我们经常需要构建工具型程序:
- 数据预处理工具
- 离线算法管线
- 图像 / 点云 / 配置文件批处理
- Unity / C# 项目的辅助命令行工具
一开始,最自然的做法是:
👉 使用 Command Pattern(命令模式)自行实现一套 CLI 调度系统
这在架构上是完全正确的。
但当命令逐渐增多、参数变复杂之后,你会发现:
我们并不是在实现“业务命令”,
而是在不断重复造一个 命令行解析框架。
这正是 System.CommandLine 存在的意义。
一、回顾:传统的 Command 模式 CLI 设计
典型的手写方案通常长这样:
- ICommand
- CommandGenerate
- CommandParse
- CommandExport
- Program.cs 中手动解析 args
- switch / dictionary 做 dispatch
这种方式的优点
- 命令解耦
- 易扩展
- 符合 Command Pattern
- 业务逻辑清晰
但问题也很明显
- 参数解析需要自己写
--help/ usage 要自己维护- 子命令嵌套成本高
- 参数校验、默认值、类型转换重复劳动
问题不在设计,而在“你做了太多框架层的工作”。
二、System.CommandLine 是什么?
一句话定义:
System.CommandLine 是微软官方提供的、
基于 Command Pattern 的命令行应用框架
它做的事情只有一件:
把“命令行解析 + 路由”这层脏活累活标准化
而你只需要关心:
- 命令是什么
- 参数是什么
- 业务逻辑是什么
三、核心设计思想(重要)
很多人误解 System.CommandLine 是“替代 Command 模式”。
实际上正好相反:
System.CommandLine 是 Command 模式在 CLI 场景下的成熟实现
你之前的设计思想是对的,只是:
- ❌ 手写了解析和调度
- ✅ 现在交给框架
四、整体结构设计(推荐)
一个工程级 CLI 工具,我推荐这样的结构:
ProjectRoot
│
├── Program.cs // CLI 装配层
│
├── Commands/ // 命令定义层
│ ├── GenerateCommand.cs
│ ├── ParseCommand.cs
│ └── ExportCommand.cs
│
├── Services/ // 核心业务
│ ├── Generator.cs
│ ├── Parser.cs
│ └── Exporter.cs
│
└── Infrastructure/
└── PathResolver.cs
👉 Command 层不写业务,只表达“意图”
五、最小可用示例
1️⃣ Program.cs:只负责注册命令
using System.CommandLine;
var root = new RootCommand("Sample CLI Tool");
root.AddCommand(GenerateCommand.Build());
root.AddCommand(ParseCommand.Build());
return await root.InvokeAsync(args);
没有 switch,没有 if,没有手写解析。
2️⃣ 一个命令 = 一个文件
using System.CommandLine;
public static class GenerateCommand
{
public static Command Build()
{
var command = new Command(
"generate",
"Generate output from input data"
);
var input = new Option<string>(
"--input",
description: "Input file path"
) { IsRequired = true };
var output = new Option<string>(
"--output",
description: "Output file path"
) { IsRequired = true };
command.AddOption(input);
command.AddOption(output);
command.SetHandler((string i, string o) =>
{
Console.WriteLine($"Generate: {i} -> {o}");
// Generator.Run(i, o);
}, input, output);
return command;
}
}
3️⃣ 自动获得的能力
不需要你写任何额外代码,就已经支持:
tool generate --input data.json --output result.bin
tool generate --help
tool --help
包括:
- 参数校验
- Help 文档
- 子命令结构
- 类型安全绑定
六、支持 --relative / --absolute 的工程实践
在实际项目中,路径处理是高频需求。
推荐做法是:
- CLI 层只接收“路径意图”
- 路径解析统一放到 Infrastructure 层
public static class PathResolver
{
public static string Resolve(string path, bool relative)
{
return relative
? Path.GetFullPath(path)
: path;
}
}
Command 里只做:
var relative = new Option<bool>("--relative", "Use relative path");
👉 这样命令层保持干净,后续也方便迁移到 GUI / Editor 工具。
七、和手写 Command 模式的关系
| 维度 | 手写 Command | System.CommandLine |
|---|---|---|
| 架构思想 | Command Pattern | Command Pattern |
| 参数解析 | 自己写 | 框架内置 |
| Help 文档 | 自维护 | 自动生成 |
| 扩展成本 | 中 | 低 |
| 工业成熟度 | 自制 | 官方 |
这不是推翻重来,而是自然升级。
八、适用场景总结
System.CommandLine 非常适合:
- 离线工具
- 算法管线
- Unity / C# 辅助工具
- 内部工程工具
- 自动化脚本入口
如果你发现自己正在:
“为了 CLI 写大量非业务代码”
那就是一个明确的信号:
👉 该用它了。
结语
命令模式本身没有问题,
问题在于:你不该在每个项目里重复造轮子。
System.CommandLine 做的不是“帮你偷懒”,
而是把精力从框架层解放出来,让你专注真正重要的事。\
作者:世纪末的魔术师
出处:https://www.cnblogs.com/Firepad-magic/
Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号