You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# System.CommandLine基于CLI选项选择处理程序的可扩展方案咨询

System.CommandLine 选项组合分支的可扩展实现方案

你现在遇到的多分支if-else膨胀问题,本质是「选项组合规则→执行逻辑」的硬编码映射问题,下面两个方案都是实际项目里验证过的,不用引入额外依赖,适配.NET的System.CommandLine生态:

方案1:规则-逻辑注册表(轻量实现,适合选项数<5的场景)

核心思路是把散落在if-else里的判断条件和对应逻辑拆成独立条目,统一放到注册表里维护,彻底消灭嵌套判断。
首先定义统一的选项上下文,把所有解析出来的选项值封装进去,后续加新选项只需要改这个类,不用动所有处理方法的参数签名:

// 全局选项上下文,新增选项直接在这里加属性
public class CliOptionContext
{
    public string? OptA { get; set; }
    public string? OptB { get; set; }
    public string? OptC { get; set; }
}

然后用元组列表把所有匹配规则和执行逻辑注册进去,启动时一次性初始化:

// 所有选项组合的处理规则都在这里注册
private static readonly List<(Func<CliOptionContext, bool> IsMatch, Action RunLogic)> _optionHandlers = new()
{
    (
        IsMatch: ctx => ctx.OptA != null && ctx.OptB != null && ctx.OptC != null,
        RunLogic: () => { Method1(); Method2(); }
    ),
    (
        IsMatch: ctx => ctx.OptA == null && ctx.OptB != null && ctx.OptC != null,
        RunLogic: () => Method3()
    ),
    (
        IsMatch: ctx => ctx.OptA != null && ctx.OptB == null && ctx.OptC != null,
        RunLogic: () => Method4()
    )
    // 后续新增分支直接在这里加条目,不需要修改已有判断逻辑
};

最后把原来的HandleOptions方法改成遍历注册表匹配即可:

internal static void HandleOptions(string aOpt, string bOpt, string cOpt)
{
    var ctx = new CliOptionContext { OptA = aOpt, OptB = bOpt, OptC = cOpt };
    var matched = _optionHandlers.FirstOrDefault(h => h.IsMatch(ctx));
    
    if (matched.RunLogic == null)
    {
        // 无匹配时的兜底逻辑,比如输出帮助文本
        Console.WriteLine("无效的选项组合,请使用--help查看使用说明");
        return;
    }
    matched.RunLogic();
}
  • 这个方案改动成本极低,不会破坏现有逻辑,比堆if-else好维护得多。注意如果存在多个规则同时匹配的情况,列表靠前的规则会优先生效,和System.CommandLine本身的命令匹配优先级逻辑一致。

方案2:责任链模式(适合选项多、组合规则复杂的场景)

如果后续选项会涨到6个以上,甚至不同组合有可复用的逻辑片段,就用责任链模式把每个判断分支拆成独立的处理器节点,按优先级串成处理链,每个节点只负责判断自己关心的规则,匹配就执行逻辑终止传递,不匹配就转给下一个节点。
首先定义处理器基类:

public abstract class CliOptionHandler
{
    private CliOptionHandler? _next;

    // 链式配置下一级处理器
    public CliOptionHandler SetNext(CliOptionHandler nextHandler)
    {
        _next = nextHandler;
        return this;
    }

    public abstract void Handle(CliOptionContext context);

    // 封装传递给下一级的公共逻辑,避免每个实现类重复写
    protected void PassToNext(CliOptionContext context) => _next?.Handle(context);
}

每个选项组合的逻辑单独写成实现类,比如三个选项全传的场景:

public class AllOptionsProvidedHandler : CliOptionHandler
{
    public override void Handle(CliOptionContext context)
    {
        if (context.OptA != null && context.OptB != null && context.OptC != null)
        {
            Method1();
            Method2();
            return;
        }
        PassToNext(context);
    }
}

public class OnlyBCOptionsHandler : CliOptionHandler
{
    public override void Handle(CliOptionContext context)
    {
        if (context.OptA == null && context.OptB != null && context.OptC != null)
        {
            Method3();
            return;
        }
        PassToNext(context);
    }
}

程序启动时把所有处理器按优先级串成链:

private static readonly CliOptionHandler _processingChain = new AllOptionsProvidedHandler()
    .SetNext(new OnlyBCOptionsHandler())
    .SetNext(new OnlyACOptionsHandler())
    // 新增处理器直接在这里链式追加即可
    .SetNext(new FallbackNoMatchHandler()); // 最后一个节点做兜底

调用时直接触发链头的处理方法就行:

internal static void HandleOptions(string aOpt, string bOpt, string cOpt)
{
    var ctx = new CliOptionContext { OptA = aOpt, OptB = bOpt, OptC = cOpt };
    _processingChain.Handle(ctx);
}
  • 额外提一句:你用的System.CommandLine本身支持直接给不同的选项配置绑定对应的处理委托,库内部会自动做参数校验和匹配,很多时候不需要自己写一层分发逻辑,可以直接利用库本身的能力减少重复代码。
  • 别过度设计:如果你的工具总共就3-4个选项,不会再大规模加功能,直接用第一种方案就行,上来就写一堆抽象类反而会增加维护成本。

内容的提问来源于stack exchange,提问作者Peepo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 23:16:30