如何阻止C#子类直接调用基类的受保护方法?
解决方案:通过钩子调用上下文检查阻止非法调用
要解决子类直接调用基类OnPrepare/OnWorking/OnCancel方法的问题,我们可以在基类中添加调用上下文检查——只有当基类在对应状态转换过程中主动调用这些钩子方法时,才允许执行逻辑;子类直接调用时抛出异常。这种方式不会破坏原有模板方法的结构,同时能严格控制钩子方法的调用时机。
具体实现思路
- 用枚举类型跟踪当前正在执行的钩子类型,确保每个钩子只能在对应的状态转换流程中被执行。
- 将原有的抽象钩子方法改为
virtual,在基类实现中添加上下文合法性检查逻辑。 - 封装私有包装方法统一处理钩子调用,负责设置/清理上下文标记,避免并发调用冲突。
- 子类重写钩子方法时必须调用
base.OnXXX()触发检查,确保只有合法调用才能执行逻辑。
修改后的完整代码
using System; using System.Threading; using System.Threading.Tasks; abstract class StateMachine { protected string State { get; private set; } // 跟踪当前正在执行的钩子类型,确保调用上下文合法 private enum ActiveHook { None, Prepare, Working, Cancel } private ActiveHook _currentActiveHook = ActiveHook.None; // 基类提供带检查的钩子方法,子类必须重写并调用base.OnXXX() protected virtual void OnPrepare() { if (_currentActiveHook != ActiveHook.Prepare) throw new InvalidOperationException("OnPrepare can only be invoked by the StateMachine during preparation."); } protected virtual void OnWorking() { if (_currentActiveHook != ActiveHook.Working) throw new InvalidOperationException("OnWorking can only be invoked by the StateMachine during execution."); } protected virtual void OnCancel() { if (_currentActiveHook != ActiveHook.Cancel) throw new InvalidOperationException("OnCancel can only be invoked by the StateMachine during cancellation."); } // 私有包装方法:负责设置上下文并调用钩子,确保执行后清理标记 private void InvokePrepareHook() { if (_currentActiveHook != ActiveHook.None) throw new InvalidOperationException("Cannot start a new state transition while another is in progress."); _currentActiveHook = ActiveHook.Prepare; try { OnPrepare(); } finally { _currentActiveHook = ActiveHook.None; } } private void InvokeWorkingHook() { if (_currentActiveHook != ActiveHook.None) throw new InvalidOperationException("Cannot start a new state transition while another is in progress."); _currentActiveHook = ActiveHook.Working; try { OnWorking(); } finally { _currentActiveHook = ActiveHook.None; } } private void InvokeCancelHook() { if (_currentActiveHook != ActiveHook.None) throw new InvalidOperationException("Cannot start a new state transition while another is in progress."); _currentActiveHook = ActiveHook.Cancel; try { OnCancel(); } finally { _currentActiveHook = ActiveHook.None; } } // 原有状态转换方法,改为调用包装后的钩子 public bool Prepare() { if(State != null) { return false; } State = "Preparing"; InvokePrepareHook(); State = "Prepared"; return true; } public bool Start() { if(State != "Prepared") { return false; } State = "Working"; InvokeWorkingHook(); State = "Done"; return true; } public bool Cancel() { if(State != "Working" || State == "Done") { return false; } InvokeCancelHook(); State = "Canceled"; return true; } } class Downloader : StateMachine { protected override void OnPrepare() { base.OnPrepare(); // 必须调用基类检查,否则非法调用会抛出异常 Console.WriteLine("I am preparing."); Thread.Sleep(1000); } protected override void OnWorking() { base.OnWorking(); Console.WriteLine("I am working."); Thread.Sleep(1000); // 尝试直接调用OnCancel()会触发异常,阻止非法逻辑 // OnCancel(); } protected override void OnCancel() { base.OnCancel(); Console.WriteLine("Let's cancel the operation!"); } } class Program { static void Main(string[] args) { Downloader downloader = new Downloader(); Parallel.Invoke(() => { downloader.Prepare(); downloader.Start(); }, () => { Thread.Sleep(1500); downloader.Cancel(); }); } }
效果说明
- 当子类在
OnWorking中尝试调用OnCancel()时,base.OnCancel()会检查当前上下文为ActiveHook.Working,与预期的ActiveHook.Cancel不符,直接抛出InvalidOperationException,彻底阻止非法调用。 - 基类在状态转换过程中调用钩子时,上下文标记正确,子类逻辑正常执行,原有状态机的状态流转逻辑不受影响。
- 若子类忘记调用
base.OnXXX(),基类主动调用钩子时也会抛出异常,强制子类遵守调用规范,避免遗漏检查。
内容的提问来源于stack exchange,提问作者peanut
相关产品推荐
相关产品推荐

