惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
云风的 BLOG
云风的 BLOG
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Announcements
Recent Announcements
IT之家
IT之家
Google DeepMind News
Google DeepMind News
罗磊的独立博客
爱范儿
爱范儿
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
U
Unit 42
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
B
Blog
博客园 - 叶小钗
月光博客
月光博客
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
C
Check Point Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

博客园 - 世纪末の魔术师

pocket_dict_5000轻量离线词表 《The Wealth Ladder》读书笔记 项目管理,真正的密码只有一句话 Linq延迟执行陷阱 坚毅,是一种缓慢修理自己的方式 语言的边界,与软件的命运 人类与AI协同进化 《坚毅》第一部分读书笔记 用 System.CommandLine 构建工程级 CLI 工具 从哎呦”到语言宇宙 ——读《What Is ChatGPT Doing … And Why Does It Work?》 ⏱️ 深入理解定时器中的【时间轮算法】 🚫 为什么「定时器」不应该是线程安全的? C# AOT编译后——调用其类库方法因顺序出错? UnitTask中的Forget()与 CTS 光线追踪和球体追踪 八、方法(method) 二十、异常与状态管理(Exception&State Management) 二十八、IO绑定的异步操作(IO-Bound Async) 二十二、CLR寄宿与AppDomain(CLR Hosting and App Domains ) 二十九、原始线程同步构造(Primitive Thread Synchronization Constructs ) 二十六、线程与并发(Thread Basic) 二十七、计算密集型异步操作(Compute-Bound Asynchronous Operations) 二十三、程序集加载与反射(Assembly Loading and Reflection)
用 Command 模式构建可扩展的命令行工具
世纪末の魔术师 · 2026-01-21 · via 博客园 - 世纪末の魔术师

用 Command 模式构建可扩展的 C# 命令行工具(支持多命令与路径解析)

在开发工具型程序(如:数据转换、图像处理、批处理工具)时,一个常见的演进过程是:

一个 Main → 一堆 if-else → 越来越难维护

本文介绍一种工程实践中非常成熟的做法:
用 Command 模式重构命令行工具,让每个功能成为一个独立命令(Command),主程序只负责调度(dispatch)。


一、问题背景:为什么要重构 CLI 结构?

传统写法通常是这样:

static void Main(string[] args)
{
    if (args[0] == "a") { /* 功能 A */ }
    else if (args[0] == "b") { /* 功能 B */ }
    else if (args[0] == "c") { /* 功能 C */ }
}

当功能增加后,会出现:

  • • Main 过于臃肿
  • • 功能之间强耦合
  • • 不利于测试与扩展
  • • 不利于长期维护

更合理的目标是:

新增一个命令,只新增一个文件
主程序不需要修改逻辑


二、设计目标

我们希望命令行工具具备以下特性:

  • • 每个功能是一个独立命令
  • • 支持命令行传参(如 -i input -o output
  • • 支持相对路径 / 绝对路径切换
  • • 主程序只负责「分发命令」

三、整体架构概览

CLI Tool
│
├── Program.cs          // 只做 dispatch
├── ICommand.cs         // Command 抽象
├── CommandContext.cs   // 参数 & 路径上下文
├── PathResolver.cs    // 路径解析
│
├── Commands/
│   ├── CommandA.cs
│   ├── CommandB.cs
│   └── CommandC.cs

四、核心思想:Command 模式

1️⃣ Command 接口

public interface ICommand
{
    string Name { get; }
    string Description { get; }
    void Execute(CommandContext context);
}
  • • Name:命令名(如 convertexport
  • • Execute:命令执行入口

2️⃣ 示例 Command(功能模块)

public class CommandExample : ICommand
{
    public string Name => "example";
    public string Description => "Run example task";

    public void Execute(CommandContext ctx)
    {
        string input  = ctx.ResolvePath("i");
        string output = ctx.ResolvePathOrDefault("o", "out.dat");

        ExampleProcessor.Run(input, output);
    }
}

👉 每个命令一个类,职责单一


五、主程序:只负责 Dispatch

class Program
{
    static readonly List<ICommand> Commands = new()
    {
        new CommandExample(),
        new CommandOther()
    };

    static void Main(string[] args)
    {
        var (cmdName, options) = ArgParser.Parse(args);

        var command = Commands.FirstOrDefault(c => c.Name == cmdName);
        if (command == null)
        {
            PrintHelp();
            return;
        }

        var context = new CommandContext(options);
        command.Execute(context);
    }
}

主程序的特点:

  • • 不包含业务逻辑
  • • 不关心参数含义
  • • 只负责:
    • • 找到 Command
    • • 调用 Execute

六、命令行参数解析(示例)

public static class ArgParser
{
    public static (string, Dictionary<string, string>) Parse(string[] args)
    {
        string command = args[0];
        var dict = new Dictionary<string, string>();

        for (int i = 1; i < args.Length - 1; i++)
        {
            if (args[i].StartsWith("-"))
                dict[args[i].TrimStart('-')] = args[++i];
        }

        return (command, dict);
    }
}

示例调用:

tool.exe example -i data/input.json -o result.bin

七、路径处理:支持相对 / 绝对模式

命令行工具中,路径问题非常常见。

CommandContext

public class CommandContext
{
    public Dictionary<string, string> Args { get; }
    public string BaseDir { get; }

    public CommandContext(Dictionary<string, string> args)
    {
        Args = args;
        BaseDir = args.ContainsKey("absolute")
            ? Directory.GetCurrentDirectory()
            : AppContext.BaseDirectory;
    }

    public string ResolvePath(string key)
    {
        return PathResolver.Resolve(Args[key], BaseDir);
    }
}

PathResolver

public static class PathResolver
{
    public static string Resolve(string path, string baseDir)
    {
        return Path.IsPathRooted(path)
            ? path
            : Path.GetFullPath(Path.Combine(baseDir, path));
    }
}

支持:

tool.exe example -i data/a.json
tool.exe example -i data/a.json --absolute

八、用到了哪些设计模式?

✅ Command Pattern(核心)

  • • 每个命令封装一个操作
  • • 主程序通过接口统一调用

✅ Strategy Pattern(弱形式)

  • • 不同 Command = 不同执行策略
  • • 运行时选择

✅ Context Object(工程实践)

  • • 参数、路径、环境信息集中管理
  • • Command 不直接依赖全局状态

九、这种结构适合什么场景?

非常适合:

  • • 数据处理工具
  • • 验证工具
  • • Unity / OpenCV / Web 辅助工具
  • • 内部工程 CLI 工具链

甚至可以无缝接入 Unity Editor 或 CI 流水线


十、总结

通过 Command 模式重构命令行工具,可以获得:

  • • 清晰的结构
  • • 易扩展、易维护
  • • 新功能零侵入
  • • 工程级可读性