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

如何阻止C#子类直接调用基类的受保护方法?

解决方案:通过钩子调用上下文检查阻止非法调用

要解决子类直接调用基类OnPrepare/OnWorking/OnCancel方法的问题,我们可以在基类中添加调用上下文检查——只有当基类在对应状态转换过程中主动调用这些钩子方法时,才允许执行逻辑;子类直接调用时抛出异常。这种方式不会破坏原有模板方法的结构,同时能严格控制钩子方法的调用时机。

具体实现思路

  1. 用枚举类型跟踪当前正在执行的钩子类型,确保每个钩子只能在对应的状态转换流程中被执行。
  2. 将原有的抽象钩子方法改为virtual,在基类实现中添加上下文合法性检查逻辑。
  3. 封装私有包装方法统一处理钩子调用,负责设置/清理上下文标记,避免并发调用冲突。
  4. 子类重写钩子方法时必须调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:17:46