State machine patterns by C#/.Net
"复杂性不是问题,失控的复杂性才是。"
| # | 项目 | 领域 | 核心概念 |
|---|---|---|---|
| 1 | 基础状态机 | 入门 | 状态、转换、事件 |
| 2 | 异步状态机 | 企业 | async/await、并发控制 |
| 3 | 嵌套状态机 | 企业 | 层次状态、子状态机 |
| 4 | 并行状态机 | 企业 | 正交区域、同步状态 |
| 5 | 历史状态机 | 企业 | 状态记忆、恢复机制 |
| 6 | 事件驱动状态机 | 企业 | 发布订阅、解耦 |
| 7 | 表驱动状态机 | 企业 | 配置化、运行时加载 |
| 8 | 持久化状态机 | 企业 | 数据库存储、断点续传 |
| 9 | Boss战状态机 | 游戏 | 多阶段、AI行为 |
| 10 | 格斗连招系统 | 游戏 | 输入缓冲、帧数据 |
| 11 | 潜行AI系统 | 游戏 | 感知系统、警戒等级 |
| 12 | 回合制战斗 | 游戏 | 行动队列、状态效果 |
| 13 | 赛车载具系统 | 游戏 | 物理状态、损坏系统 |
状态机是一种思维模型,它告诉我们:
任何复杂系统,在任意时刻,都处于某个明确的状态;
系统只能通过定义好的转换,从一个状态迁移到另一个状态。
这个简单的约束,带来了强大的控制力。
┌─────────────────────────────────────────┐
│ 复杂性的来源 │
└─────────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 状态爆炸 │ │ 转换混乱 │ │ 边界模糊 │
│ │ │ │ │ │
│ if-else │ │ 意外转换 │ │ 非法状态 │
│ 嵌套地狱 │ │ 难以追踪 │ │ 竞态条件 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 状态机的解决方案 │
│ │
│ ✅ 状态明确定义 │
│ ✅ 转换显式声明 │
│ ✅ 非法路径不存在 │
│ ✅ 行为与状态绑定 │
└─────────────────────────────────────────┘
传统思维: "订单有很多字段,我们检查字段来判断状态"
状态机思维: "订单本身就是状态的载体,状态决定可用操作"
┌────────────────────────────────────────────────────────────────┐
│ │
│ Created → Paid → Processing → Shipped → Delivered → Closed │
│ │ │ │
│ └──────── Cancelled ◄──────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
启发: 状态不是派生属性,而是系统的第一公民
审批流、报销流、发布流...
传统做法: 硬编码每个节点的判断逻辑
状态机做法: 流程即配置,状态即节点,转换即连线
启发: 将"流程"从代码中解放出来,变成可配置的数据
Saga模式本质上就是分布式状态机:
LocalTransaction₁ → LocalTransaction₂ → LocalTransaction₃
│ │ │
▼ ▼ ▼
Compensate₁ ◄───── Compensate₂ ◄───── Compensate₃
启发: 复杂的分布式事务,可以用状态机建模和恢复
IoT设备状态: Offline → Connecting → Online → Working → Error
每个状态有明确的:
- 允许的操作
- 监控指标
- 告警规则
- 恢复策略
启发: 状态机让设备行为可预测、可监控、可恢复
最经典的状态机例子:
┌──────┐ 60秒 ┌──────┐ 5秒 ┌──────┐
│ 🟢 │ ────────→ │ 🟡 │ ────────→ │ 🔴 │
│ 绿灯 │ │ 黄灯 │ │ 红灯 │
└──────┘ └──────┘ └──────┘
▲ │
│ 60秒 │
└────────────────────────────────────┘
启发: 明确的状态 + 明确的规则 = 安全的系统
童年 → 青年 → 壮年 → 老年
每个阶段:
- 有其独特的责任和权利
- 有向下一阶段的转换条件
- 无法跳跃或逆转(物理意义上)
启发: 状态机帮助我们接受"阶段性"思维
当前状态决定当前选择,而非无限可能
平静 ──(刺激)──→ 愤怒 ──(冷静)──→ 反思 ──(释然)──→ 平静
│ │
│ └──(失控)──→ 后悔
│ │
└───────────────(时间)───────────┘
启发: 认识到情绪是"状态"而非"本质"
我们可以设计"转换规则"来管理情绪
┌─────────────────────┐
│ 舒适区 (默认) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ 尝试 (行动) │◄──┐
└──────────┬──────────┘ │
│ │
┌────────────────┼────────────┐ │
│ │ │ │
┌─────────▼───────┐ ┌──────▼─────┐ │ │
│ 放弃 (回退) │ │ 坚持 │─────┘ │
│ │ └──────┬─────┘ │
└────────┬────────┘ │ │
│ │ │
│ ┌────────▼────────┐ │
│ │ 习惯 (新常态) │ │
│ └─────────────────┘ │
│ │
└───────────────────────────────┘
启发: 改变是状态转换,需要足够的"能量"突破阈值
一旦进入新状态,维持成本降低
状态机: 适合有限、明确的行为模式
例: 守卫的 巡逻→怀疑→警觉→战斗
行为树: 适合复杂、层次化的决策
例: 战略AI的多目标权衡
混合方案: 行为树做决策,状态机做执行
"决定做什么" vs "怎么做"
Idle ←→ Walk ←→ Run
↓ ↓ ↓
Jump Jump Jump
↓ ↓ ↓
Fall Fall Fall
↓ ↓ ↓
Land Land Land
动画混合 = 状态间的插值过渡
动画事件 = 状态中的时间触发器
启发: 状态机天然适合处理"互斥"的行为
人不能同时站立和奔跑
Loading → MainMenu → Playing → Paused → GameOver
↑ │
└─────────┘
每个状态管理自己的:
- UI层级
- 输入响应
- 资源加载
- 音频控制
启发: 状态机是游戏架构的骨架
客户端状态机 + 服务端权威 + 状态预测 + 回滚
Predicted → Confirmed
↓ ↓
Rollback ← Mismatch
启发: 分布式系统的一致性,本质是状态同步
❌ 错误: isMoving && isAttacking && isJumping (同时为真?)
✅ 正确: State = { Idle, Moving, Attacking, JumpAttacking }
每个状态组合应该是一个独立状态
❌ 错误: 部分完成的状态转换
✅ 正确: 要么完全转换,要么保持原状态
状态转换是事务
❌ 错误: 外部检查状态然后执行对应逻辑
✅ 正确: 状态自己知道该做什么
switch(state) { vs state.Update()
case A: doA(); break; state.HandleInput()
case B: doB(); break; state.Render()
}
❌ 错误: "当差不多的时候转换"
✅ 正确: "当HP < 30% && 距离 > 10m时转换到Flee状态"
模糊的条件导致不可预测的行为
一切有"生命"的事物,都可以用状态机建模:
进程: Created → Ready → Running → Waiting → Terminated
连接: Closed → Opening → Open → Closing → Closed
事务: Pending → Processing → Committed/Rolledback
请求: Sent → Received → Processing → Responded
状态机本质上是对"时间流逝"的建模:
过去 ─────────────────────────────────────→ 未来
│ │ │ │
状态A → 转换 → 状态B → 转换 → ...
时间是单向的,状态转换也应该有方向性
(虽然可以"返回"某个状态,但那是新的转换,不是时间回溯)
相同的初始状态 + 相同的事件序列 = 相同的最终状态
这就是为什么状态机:
✅ 易于测试 (可重放)
✅ 易于调试 (可追踪)
✅ 易于理解 (可预测)
"我不能同时处于所有状态"
这不是限制,而是解放。
有限的状态意味着有限的复杂度。
"从A到C,必须经过B"
不是所有目标都能直达。
状态机帮助我们设计必经之路。
"当前状态决定当前可能"
过去的状态已经退出,未来的状态尚未进入。
专注于当前状态的职责。
"变化是必然的,但变化是有规则的"
我们无法阻止变化,但可以设计变化的规则。
好的状态机让变化可控、可预测、可追溯。
状态机不仅是一个编程模式,更是一种思维方式:
当你面对复杂系统时,问自己:
1. 这个系统有哪些互斥的状态?
2. 每个状态下,系统应该表现出什么行为?
3. 什么条件触发状态转换?
4. 哪些转换是允许的,哪些是禁止的?
5. 如何处理意外情况和错误恢复?
回答完这些问题,你就有了状态机的骨架。
"简单的规则产生复杂的行为, 复杂的规则产生混乱的行为。"
— 复杂系统理论
状态机的魔力在于:用简单、明确的规则,构建出复杂但可控的系统。
无论是一个订单的生命周期,还是一个Boss的战斗AI; 无论是一段感情的发展,还是一个产品的迭代; 状态机的思维都能帮助我们:
看清当下,规划转换,走向期望的状态。
本项目包含13个状态机示例,涵盖企业应用和游戏开发两大领域。 每个示例都是独立可运行的 .NET 10 / C# 14 项目。
# 运行任意示例
cd "1. 基础状态机"
dotnet runHappy State Machining! 🎯