C# 责任链模式实现方法 C#如何构建处理请求的链条

来源:这里教程网 时间:2026-02-21 17:41:55 作者:

如何用抽象类定义统一的 Handler 接口

责任链的核心是让每个处理器能处理请求,也能把请求传给下一个。C# 中最稳妥的方式是定义一个抽象基类

Handler
,而不是接口——因为接口无法提供默认的
SetNext
和委托调用逻辑,容易导致重复代码或空指针风险。

关键点在于: -

Handler
必须持有对下一个
Handler
的引用(可为空) - 处理方法(如
Handle
)应返回
bool
object
表示是否终结链条,避免隐式跳过后续节点 -
SetNext
应返回
this
,支持链式调用(如
h1.SetNext(h2).SetNext(h3)
public abstract class Handler
{
    protected Handler? _next;
    public Handler SetNext(Handler next) { _next = next; return this; }
    public virtual bool Handle(string request)
    {
        return _next?.Handle(request) == true;
    }
}

具体 Handler 如何判断是否处理并决定是否继续

每个子类重写

Handle
时,必须显式判断当前请求是否属于自己的职责范围;若不处理,不能直接 return false,而应调用
_next?.Handle(...)
,否则链条会意外中断。

常见错误: - 在条件不满足时写

return false;
,导致后续 Handler 完全没机会执行 - 忘记检查
_next
是否为 null,引发
NullReferenceException
- 把业务逻辑和“是否继续”耦合太紧,比如用
if (x) { ... } else { return _next?.Handle(...) }
,但漏掉对
_next
的空值保护

推荐写法: - 用

if (CanHandle(request)) { ... return true; }
明确职责边界 - 结尾统一用
return _next?.Handle(request) == true;
,既安全又语义清晰 - 若需透传结果(如修改后的 request),可改用
string? Handle(string request)
,返回 null 表示终止

如何组装链条并避免循环引用或遗漏

手动串联时最容易出错的是顺序颠倒、漏设

SetNext
,或无意中形成环(比如
a.SetNext(b); b.SetNext(a);
)。生产环境建议用工厂或配置驱动构建。

实操建议: - 链条起点必须是非 null 的第一个 Handler,且终点 Handler 的

_next
必须为 null - 单元测试里加断言:遍历链条长度 ≤ 预期数,且无重复实例(可用
Object.ReferenceEquals
检查) - 如果 Handler 有状态(如计数器、缓存),注意多线程下是否线程安全;无状态 Handler 可复用,有状态的建议每次新建 - 不要用静态字段保存链条,会导致不同请求互相干扰

为什么不用委托链或 LINQ Aggregate 实现

有人尝试用

Func<string bool></string>
数组 +
Aggregate
模拟责任链,看起来简洁,但实际问题不少:

硬伤包括: - 无法在中途修改请求内容(委托签名固定) - 调试困难:堆栈里全是

Aggregate
内部帧,看不出哪个 Handler 出了问题 - 无法动态插入/跳过某个 Handler(比如根据配置开关日志 Handler) - 每次调用都重新遍历整个数组,无短路优化,性能不如原生引用跳转

委托适合简单过滤场景(如中间件管道),但真正需要“职责分离 + 动态协作 + 可调试”的业务逻辑链,还是老老实实用类继承更可控。

责任链不是越“链”越好,关键是每个

Handler
CanHandle
判断逻辑是否足够轻量,以及链条长度是否在可预期范围内——超过 5–6 层时,就得考虑是不是职责划分过细,或者该用策略模式+路由表替代了。

相关推荐