c#状态模式核心是state接口、context类、状态切换逻辑三者对齐;接口方法须按触发场景拆分并由context主动调用;context需管控切换入口、校验与委托;concretestate应仅关注自身行为,通过返回值或事件通知context切换。

直接说结论:C#里实现状态模式,核心不是“写多少类”,而是把 State 接口、Context 类、状态切换逻辑这三块对齐,否则容易写成一堆互相持有引用的“伪状态”。
State 接口该定义哪些方法
别一上来就照搬 Handle() 或 Execute()。接口方法必须和实际行为强绑定,且由 Context 主动调用——不是状态自己“决定做什么”,而是 Context 在某个时机(比如用户点击、帧更新、消息到达)调用状态的方法。
- 常见错误:接口只定义一个
DoWork(),结果所有状态都挤在同一个方法里做判断,又绕回 if-else - 推荐做法:按触发场景拆,比如
OnEnter()、OnUpdate()、OnExit()(Unity 常见),或InsertCoin()、EjectCoin()、TurnCrank()(糖果机示例) - 注意:如果某些状态根本不响应某个操作(比如“已关机”状态下不处理
TurnCrank()),接口仍要声明,但具体实现可抛NotSupportedException或直接 return
Context 类最容易漏掉的关键逻辑
Context 不是状态容器,它是状态的“调度员”。它必须控制状态切换的入口、校验和委托链,否则状态会失控。
- 必须提供明确的状态变更入口,比如
ChangeState(IState newState),而不是让外部直接赋值currentState = ... - 切换前应检查是否允许(例如“正在保存中”不能立刻切到“已发布”),否则可能跳过中间状态导致数据不一致
- 每次调用状态方法前,建议加空引用检查:
if (currentState != null) currentState.OnUpdate();,避免 NullReferenceException - 不要在
Context里缓存状态相关的业务数据(如文档内容、玩家血量),这些属于领域模型,应由Context持有并传给状态,而非让状态自己去查
ConcreteState 类怎么避免“状态污染”
每个具体状态类应该只关心“自己这一段”的行为,不感知其他状态的存在,更不能反向调用 Context.ChangeState() 来切自己——这会造成隐式依赖和循环调用风险。
- 正确做法:状态类通过返回值或事件通知
Context,“我处理完了,下一步该切到 X 状态”,由Context统一决策和执行切换 - 举例:在
HasQuarterState.TurnCrank()里不做context.ChangeState(new SoldState()),而是返回SoldState类型或触发OnSold事件 - 构造函数参数要克制:只传
Context实例或必要依赖(如PlayerController),别塞一堆服务或配置,否则状态类变成“上帝对象” - 如果多个状态共用某段逻辑(比如播放音效、写日志),抽成独立服务,别用继承或静态方法硬耦合
真正难的不是写出四个状态类,而是在状态切换边界处做对判断——比如“保存失败后该回到编辑中,还是维持已保存?”,这种业务语义必须落在 Context 的切换逻辑里,不能靠状态类自己猜。










