如何在C#方法链中确保特定方法(如Throw())被调用?
问题
我想要在C#中创建一个使用方法链的API,确保在方法链末尾必须调用特定方法(例如Throw()),若未调用则抛出异常。
以下是我尝试实现的简化示例:
public interface IChainable { IChainable Step1(); IChainable Step2(); void Throw(); } public class Chainable : IChainable, IDisposable { private bool _throwCalled = false; public IChainable Method1() { return this; } public IChainable Method2() { return this; } public void Throw() { _throwCalled = true; } public void Dispose() { if (!_throwCalled) { throw new InvalidOperationException("Throw() method must be called when finishing the chaining"); } } }
但我希望避免这种实现,因为它需要使用using块,而这在当前场景并不合适:
using (var chainable = new Chainable()) { chainable.Step1().Step2(); // chainable.Throw(); // 若用户忘记调用Throw(),using块结束时会抛出异常 }
我正在寻找更健壮、确定的方法,确保Throw()在方法链末尾被调用且无需using块,是否有相关技术或模式可高效实现?
解决方案
分阶段接口模式(推荐)
核心是通过拆分接口划分方法链的构建阶段和终止阶段:构建阶段的接口只暴露链式操作方法,终止阶段的接口仅包含必须调用的Throw(),通过返回值类型切换强制用户完成终止步骤。
示例实现:
// 构建阶段:仅提供链式操作方法 public interface IChainBuilder { IChainBuilder Step1(); IChainBuilder Step2(); // 最后一个构建方法返回终止接口,切换阶段 IChainTerminator FinalizeBuild(); } // 终止阶段:仅提供必须调用的结束方法 public interface IChainTerminator { void Throw(); } public class Chain : IChainBuilder, IChainTerminator { public IChainBuilder Step1() { // 执行Step1业务逻辑 return this; } public IChainBuilder Step2() { // 执行Step2业务逻辑 return this; } public IChainTerminator FinalizeBuild() { // 完成构建前的收尾逻辑 return this; } public void Throw() { // 执行最终逻辑并抛出异常 throw new InvalidOperationException("触发预设业务异常"); } }
使用方式:
// 必须按 构建方法 → FinalizeBuild() → Throw() 的顺序调用,否则无法完成有效流程 new Chain().Step1().Step2().FinalizeBuild().Throw(); // 错误示例:只调用到FinalizeBuild(),后续没有Throw(),代码无实际执行结果(IChainTerminator无其他方法) // new Chain().Step1().FinalizeBuild();
该方案优势:
- 编译期引导约束:用户若遗漏
Throw(),要么代码无意义,要么无法继续操作 - 无需依赖
IDisposable和using块,逻辑更直观 - 接口职责单一,符合SOLID设计原则
单类型引导方案
如果不想拆分多个接口,可以设计一个单类型,让所有链式方法返回自身,仅Throw()为void方法,同时通过私有构造强制用户从静态入口创建实例,引导用户必须调用Throw()完成流程:
public class ChainHandler { // 私有构造,强制通过静态方法创建实例 private ChainHandler() { } public static ChainHandler Create() => new ChainHandler(); public ChainHandler Step1() { // 执行Step1逻辑 return this; } public ChainHandler Step2() { // 执行Step2逻辑 return this; } public void Throw() { // 执行最终逻辑并抛出异常 throw new InvalidOperationException("触发业务异常"); } }
使用方式:
ChainHandler.Create().Step1().Step2().Throw();
这种方案没有编译期强制,但通过API设计引导用户完成终止步骤——若不调用Throw(),整个链式调用没有任何实际终止逻辑,用户很容易发现遗漏。结合清晰的API注释,效果同样不错。
编译期强约束进阶方案
如果需要极致的编译期检查,可以利用C#泛型约束和状态标记类型,完全限制调用顺序:
// 状态标记接口,用于区分构建状态 public interface IUnbuilt { } public interface IBuilt { } public class Chain<TState> where TState : class { private Chain() { } // 静态入口,初始状态为IUnbuilt public static Chain<IUnbuilt> Create() => new Chain<IUnbuilt>(); // 仅当状态为IUnbuilt时,才能调用链式方法 public Chain<IUnbuilt> Step1() where TState : IUnbuilt { // 执行Step1逻辑 return this; } public Chain<IUnbuilt> Step2() where TState : IUnbuilt { // 执行Step2逻辑 return this; } // 切换状态为IBuilt,仅允许IUnbuilt状态调用 public Chain<IBuilt> CompleteBuild() where TState : IUnbuilt { return this as Chain<IBuilt>; } // 仅当状态为IBuilt时,才能调用Throw() public void Throw() where TState : IBuilt { throw new InvalidOperationException("触发预设异常"); } }
使用方式:
// 正确调用:严格遵循 初始化 → 构建 → 完成构建 → 抛出 的顺序 Chain.Create().Step1().Step2().CompleteBuild().Throw(); // 错误示例:未完成构建就调用Throw(),编译直接报错 // Chain.Create().Step1().Throw(); // 错误示例:完成构建后不调用Throw(),无法进行其他操作,代码无意义 // Chain.Create().Step1().CompleteBuild();
该方案完全在编译期约束了调用流程,确保Throw()必须被调用,缺点是实现相对繁琐,适合对API严谨性要求极高的场景。
内容的提问来源于stack exchange,提问作者Andrey Namikaze
相关产品推荐
相关产品推荐

