一、状态机的核心概念
1. 什么是状态机?
状态机(Finite State Machine, FSM,又称有限状态机)是一种数学模型和编程思想,用于描述一个对象(或系统)在其生命周期内的有限个状态,以及这些状态之间的转移规则、触发转移的事件 / 条件,还有状态转移过程中(或处于某个状态时)需要执行的动作 / 行为。
简单理解:它就像对象的 “行为剧本”,规定了对象在什么状态下,遇到什么事情,应该切换到什么新状态,同时要做什么事。
2. 状态机的核心组成要素
一个完整的有限状态机包含 4 个核心部分,缺一不可:
- 状态(State):对象的离散状态,数量有限。例如 “电梯停止”“电梯上行”“电梯下行”;“订单待支付”“订单已支付”“订单已取消”。
- 事件(Event/Trigger):触发状态转移的外部或内部条件 / 信号。例如 “乘客按下 5 楼按钮”(触发电梯上行)、“用户完成支付”(触发订单从待支付→已支付)。
- 转移(Transition):从一个状态切换到另一个状态的规则,通常是 “当前状态 + 事件→目标状态”。例如 “电梯停止(当前状态)+ 按下 5 楼按钮(事件)→ 电梯上行(目标状态)”。
- 动作(Action):在状态转移过程中(或进入 / 退出某个状态时)执行的逻辑。例如 “电梯切换到上行状态时,启动电机并播报‘前往 5 楼’”、“订单变为已支付时,生成物流单号并发送通知”。
3. 状态机的核心价值(解决什么问题)
在编程中,状态机主要解决复杂状态切换的逻辑混乱问题,具体价值体现在:
- 消除大量嵌套
if/else或switch/case,让状态相关逻辑更清晰、更易维护; - 明确状态转移规则,减少 “非法状态” 出现的概率(例如订单已取消后,无法再触发支付);
- 状态与行为解耦,便于扩展新状态和新转移规则(符合开闭原则);
- 逻辑可追溯性强,每个状态的行为和转移都有明确定义,便于调试。
二、C# 中状态机的两种实现方式
下面以电商订单状态流转为场景(最经典的状态机应用场景之一),讲解两种常用实现方式。
场景定义(统一约束)
先明确订单的核心状态、事件和转移规则:
| 核心状态(State) | 触发事件(Event) | 目标状态 | 执行动作(Action) |
| 待支付(Pending) | 用户支付成功 | 已支付(Paid) | 生成物流单号、发送支付成功通知 |
| 待支付(Pending) | 用户取消订单 | 已取消(Cancelled) | 释放库存、发送取消通知 |
| 已支付(Paid) | 商家发货 | 已发货(Shipped) | 更新物流信息、发送发货通知 |
| 已发货(Shipped) | 用户确认收货 | 已完成(Completed) | 完成订单结算、关闭交易 |
前置准备:定义枚举(状态和事件)
首先用 C# 枚举明确订单的状态和触发事件,提高代码可读性和类型安全性:
using System; using System.Collections.Generic; // 订单状态枚举 public enum OrderState { Pending, // 待支付 Paid, // 已支付 Shipped, // 已发货 Completed, // 已完成 Cancelled // 已取消 } // 订单事件枚举(触发状态转移的信号) public enum OrderEvent { PaySuccess, // 支付成功 CancelOrder, // 取消订单 ShipGoods, // 商家发货 ConfirmReceipt // 用户确认收货 }方式 1:简单状态机(基于Dictionary+ 委托,适合简单场景)
这种方式无需复杂的类结构,通过字典存储 “当前状态 + 事件→转移规则”,配合委托封装动作,实现轻量级状态机。
/// <summary> /// 简单订单状态机(基于Dictionary+委托) /// </summary> public class SimpleOrderStateMachine { // 当前订单状态 public OrderState CurrentState { get; private set; } // 订单编号(附加信息) public string OrderId { get; } // 定义转移规则的委托:参数(事件),返回值(是否转移成功) private delegate bool StateTransitionDelegate(OrderEvent @event); // 存储所有状态的转移逻辑:Key=当前状态,Value=该状态对应的转移委托 private readonly Dictionary<OrderState, StateTransitionDelegate> _stateTransitions; /// <summary> /// 构造函数:初始化订单状态机 /// </summary> /// <param name="orderId">订单编号</param> public SimpleOrderStateMachine(string orderId) { OrderId = orderId; CurrentState = OrderState.Pending; // 初始状态为待支付 // 初始化状态转移规则 _stateTransitions = new Dictionary<OrderState, StateTransitionDelegate>() { { OrderState.Pending, HandlePendingState }, { OrderState.Paid, HandlePaidState }, { OrderState.Shipped, HandleShippedState }, { OrderState.Completed, HandleCompletedState }, { OrderState.Cancelled, HandleCancelledState } }; } /// <summary> /// 触发事件,驱动状态机转移 /// </summary> /// <param name="event">触发事件</param> /// <returns>转移是否成功</returns> public bool TriggerEvent(OrderEvent @event) { if (_stateTransitions.TryGetValue(CurrentState, out var transitionDelegate)) { return transitionDelegate(@event); } Console.WriteLine($"【订单{OrderId}】未知状态{CurrentState},无法处理事件{@event}"); return false; } #region 各状态的转移逻辑处理(核心:状态+事件→转移+动作) /// <summary> /// 处理“待支付”状态的转移逻辑 /// </summary> private bool HandlePendingState(OrderEvent @event) { switch (@event) { case OrderEvent.PaySuccess: // 执行状态转移前/后的动作 Console.WriteLine($"【订单{OrderId}】开始处理支付成功逻辑..."); GenerateLogisticsNo(); // 生成物流单号 SendNotification("支付成功,订单已确认"); // 发送通知 // 更新当前状态 CurrentState = OrderState.Paid; Console.WriteLine($"【订单{OrderId}】状态已从{OrderState.Pending}→{OrderState.Paid}"); return true; case OrderEvent.CancelOrder: Console.WriteLine($"【订单{OrderId}】开始处理取消订单逻辑..."); ReleaseStock(); // 释放库存 SendNotification("订单已取消,库存已释放"); CurrentState = OrderState.Cancelled; Console.WriteLine($"【订单{OrderId}】状态已从{OrderState.Pending}→{OrderState.Cancelled}"); return true; default: Console.WriteLine($"【订单{OrderId}】待支付状态无法处理事件{@event}"); return false; } } /// <summary> /// 处理“已支付”状态的转移逻辑 /// </summary> private bool HandlePaidState(OrderEvent @event) { if (@event == OrderEvent.ShipGoods) { Console.WriteLine($"【订单{OrderId}】开始处理商家发货逻辑..."); UpdateLogisticsInfo("顺丰速运:SF1234567890"); SendNotification("商家已发货,请注意查收"); CurrentState = OrderState.Shipped; Console.WriteLine($"【订单{OrderId}】状态已从{OrderState.Paid}→{OrderState.Shipped}"); return true; } Console.WriteLine($"【订单{OrderId}】已支付状态无法处理事件{@event}"); return false; } /// <summary> /// 处理“已发货”状态的转移逻辑 /// </summary> private bool HandleShippedState(OrderEvent @event) { if (@event == OrderEvent.ConfirmReceipt) { Console.WriteLine($"【订单{OrderId}】开始处理确认收货逻辑..."); CompleteOrderSettlement(); SendNotification("订单已完成,感谢您的购买"); CurrentState = OrderState.Completed; Console.WriteLine($"【订单{OrderId}】状态已从{OrderState.Shipped}→{OrderState.Completed}"); return true; } Console.WriteLine($"【订单{OrderId}】已发货状态无法处理事件{@event}"); return false; } /// <summary> /// 处理“已完成”状态的转移逻辑(终态,无后续转移) /// </summary> private bool HandleCompletedState(OrderEvent @event) { Console.WriteLine($"【订单{OrderId}】订单已完成,无法处理任何事件{@event}"); return false; } /// <summary> /// 处理“已取消”状态的转移逻辑(终态,无后续转移) /// </summary> private bool HandleCancelledState(OrderEvent @event) { Console.WriteLine($"【订单{OrderId}】订单已取消,无法处理任何事件{@event}"); return false; } #endregion #region 订单相关动作(业务逻辑) private void GenerateLogisticsNo() { Console.WriteLine($"【订单{OrderId}】生成物流单号:LOG{Guid.NewGuid():N.Substring(0, 10)}"); } private void SendNotification(string message) { Console.WriteLine($"【订单{OrderId}】发送通知:{message}"); } private void ReleaseStock() { Console.WriteLine($"【订单{OrderId}】释放商品库存成功"); } private void UpdateLogisticsInfo(string logisticsInfo) { Console.WriteLine($"【订单{OrderId}】更新物流信息:{logisticsInfo}"); } private void CompleteOrderSettlement() { Console.WriteLine($"【订单{OrderId}】完成订单结算,交易关闭"); } #endregion }测试代码(使用状态机)
class Program { static void Main(string[] args) { // 1. 创建简单订单状态机实例 var orderStateMachine = new SimpleOrderStateMachine("ORDER_20260112_001"); // 2. 按业务流程触发事件,驱动状态转移 Console.WriteLine("===== 第一步:用户支付订单 ====="); orderStateMachine.TriggerEvent(OrderEvent.PaySuccess); Console.WriteLine("\n===== 第二步:商家发货 ====="); orderStateMachine.TriggerEvent(OrderEvent.ShipGoods); Console.WriteLine("\n===== 第三步:用户确认收货 ====="); orderStateMachine.TriggerEvent(OrderEvent.ConfirmReceipt); Console.WriteLine("\n===== 第四步:尝试在已完成状态下取消订单(非法操作) ====="); orderStateMachine.TriggerEvent(OrderEvent.CancelOrder); Console.ReadKey(); } }运行结果
===== 第一步:用户支付订单 ===== 【订单ORDER_20260112_001】开始处理支付成功逻辑... 【订单ORDER_20260112_001】生成物流单号:LOGa1b2c3d4e5 【订单ORDER_20260112_001】发送通知:支付成功,订单已确认 【订单ORDER_20260112_001】状态已从Pending→Paid ===== 第二步:商家发货 ===== 【订单ORDER_20260112_001】开始处理商家发货逻辑... 【订单ORDER_20260112_001】更新物流信息:顺丰速运:SF1234567890 【订单ORDER_20260112_001】发送通知:商家已发货,请注意查收 【订单ORDER_20260112_001】状态已从Paid→Shipped ===== 第三步:用户确认收货 ===== 【订单ORDER_20260112_001】开始处理确认收货逻辑... 【订单ORDER_20260112_001】完成订单结算,交易关闭 【订单ORDER_20260112_001】发送通知:订单已完成,感谢您的购买 【订单ORDER_20260112_001】状态已从Shipped→Completed ===== 第四步:尝试在已完成状态下取消订单(非法操作) ===== 【订单ORDER_20260112_001】订单已完成,无法处理任何事件CancelOrder方式 2:面向对象状态机(基于状态模式,适合复杂场景)
这种方式是状态模式(State Pattern)的典型应用,将每个状态封装为独立的类,状态的转移逻辑和动作都内聚在对应状态类中,符合 “单一职责原则”,扩展性更强(新增状态只需新增一个状态类,无需修改原有代码)。
步骤 1:定义状态接口(统一所有状态的行为)
/// <summary> /// 订单状态接口(统一所有订单状态的行为) /// </summary> public interface IOrderState { /// <summary> /// 处理当前状态下的事件,驱动状态转移 /// </summary> /// <param name="orderContext">订单上下文(持有状态机的核心信息)</param> /// <param name="event">触发事件</param> void Handle(OrderContext orderContext, OrderEvent @event); }步骤 2:定义订单上下文(封装状态机的公共信息和状态切换)
/// <summary> /// 订单上下文(状态机的载体,持有当前状态和公共信息) /// </summary> public class OrderContext { // 当前订单状态(面向接口编程) public IOrderState CurrentState { get; set; } // 订单编号 public string OrderId { get; } // 构造函数:初始化上下文 public OrderContext(string orderId) { OrderId = orderId; // 初始状态为待支付 CurrentState = new PendingState(); } /// <summary> /// 触发事件,委托给当前状态处理 /// </summary> /// <param name="event">触发事件</param> public void TriggerEvent(OrderEvent @event) { CurrentState.Handle(this, @event); } #region 订单公共动作(供各状态类调用) public void GenerateLogisticsNo() { Console.WriteLine($"【订单{OrderId}】生成物流单号:LOG{Guid.NewGuid():N.Substring(0, 10)}"); } public void SendNotification(string message) { Console.WriteLine($"【订单{OrderId}】发送通知:{message}"); } public void ReleaseStock() { Console.WriteLine($"【订单{OrderId}】释放商品库存成功"); } public void UpdateLogisticsInfo(string logisticsInfo) { Console.WriteLine($"【订单{OrderId}】更新物流信息:{logisticsInfo}"); } public void CompleteOrderSettlement() { Console.WriteLine($"【订单{OrderId}】完成订单结算,交易关闭"); } #endregion }步骤 3:实现各个具体状态类(内聚对应状态的逻辑)
#region 具体状态类 /// <summary> /// 待支付状态 /// </summary> public class PendingState : IOrderState { public void Handle(OrderContext orderContext, OrderEvent @event) { switch (@event) { case OrderEvent.PaySuccess: Console.WriteLine($"【订单{orderContext.OrderId}】开始处理支付成功逻辑..."); orderContext.GenerateLogisticsNo(); orderContext.SendNotification("支付成功,订单已确认"); // 切换到已支付状态 orderContext.CurrentState = new PaidState(); Console.WriteLine($"【订单{orderContext.OrderId}】状态已从待支付→已支付"); break; case OrderEvent.CancelOrder: Console.WriteLine($"【订单{orderContext.OrderId}】开始处理取消订单逻辑..."); orderContext.ReleaseStock(); orderContext.SendNotification("订单已取消,库存已释放"); // 切换到已取消状态 orderContext.CurrentState = new CancelledState(); Console.WriteLine($"【订单{orderContext.OrderId}】状态已从待支付→已取消"); break; default: Console.WriteLine($"【订单{orderContext.OrderId}】待支付状态无法处理事件{@event}"); break; } } } /// <summary> /// 已支付状态 /// </summary> public class PaidState : IOrderState { public void Handle(OrderContext orderContext, OrderEvent @event) { if (@event == OrderEvent.ShipGoods) { Console.WriteLine($"【订单{orderContext.OrderId}】开始处理商家发货逻辑..."); orderContext.UpdateLogisticsInfo("顺丰速运:SF1234567890"); orderContext.SendNotification("商家已发货,请注意查收"); orderContext.CurrentState = new ShippedState(); Console.WriteLine($"【订单{orderContext.OrderId}】状态已从已支付→已发货"); return; } Console.WriteLine($"【订单{orderContext.OrderId}】已支付状态无法处理事件{@event}"); } } /// <summary> /// 已发货状态 /// </summary> public class ShippedState : IOrderState { public void Handle(OrderContext orderContext, OrderEvent @event) { if (@event == OrderEvent.ConfirmReceipt) { Console.WriteLine($"【订单{orderContext.OrderId}】开始处理确认收货逻辑..."); orderContext.CompleteOrderSettlement(); orderContext.SendNotification("订单已完成,感谢您的购买"); orderContext.CurrentState = new CompletedState(); Console.WriteLine($"【订单{orderContext.OrderId}】状态已从已发货→已完成"); return; } Console.WriteLine($"【订单{orderContext.OrderId}】已发货状态无法处理事件{@event}"); } } /// <summary> /// 已完成状态(终态) /// </summary> public class CompletedState : IOrderState { public void Handle(OrderContext orderContext, OrderEvent @event) { Console.WriteLine($"【订单{orderContext.OrderId}】订单已完成,无法处理任何事件{@event}"); } } /// <summary> /// 已取消状态(终态) /// </summary> public class CancelledState : IOrderState { public void Handle(OrderContext orderContext, OrderEvent @event) { Console.WriteLine($"【订单{orderContext.OrderId}】订单已取消,无法处理任何事件{@event}"); } } #endregion步骤 4:测试面向对象状态机
class Program { static void Main(string[] args) { // 1. 创建订单上下文(状态机载体) var orderContext = new OrderContext("ORDER_20260112_002"); // 2. 触发事件,驱动状态转移 Console.WriteLine("===== 第一步:用户支付订单 ====="); orderContext.TriggerEvent(OrderEvent.PaySuccess); Console.WriteLine("\n===== 第二步:商家发货 ====="); orderContext.TriggerEvent(OrderEvent.ShipGoods); Console.WriteLine("\n===== 第三步:用户确认收货 ====="); orderContext.TriggerEvent(OrderEvent.ConfirmReceipt); Console.WriteLine("\n===== 第四步:尝试在已完成状态下取消订单(非法操作) ====="); orderContext.TriggerEvent(OrderEvent.CancelOrder); Console.ReadKey(); } }运行结果(与简单状态机一致,扩展性更强)
===== 第一步:用户支付订单 ===== 【订单ORDER_20260112_002】开始处理支付成功逻辑... 【订单ORDER_20260112_002】生成物流单号:LOGf6g7h8i9j0 【订单ORDER_20260112_002】发送通知:支付成功,订单已确认 【订单ORDER_20260112_002】状态已从待支付→已支付 ===== 第二步:商家发货 ===== 【订单ORDER_20260112_002】开始处理商家发货逻辑... 【订单ORDER_20260112_002】更新物流信息:顺丰速运:SF1234567890 【订单ORDER_20260112_002】发送通知:商家已发货,请注意查收 【订单ORDER_20260112_002】状态已从已支付→已发货 ===== 第三步:用户确认收货 ===== 【订单ORDER_20260112_002】开始处理确认收货逻辑... 【订单ORDER_20260112_002】完成订单结算,交易关闭 【订单ORDER_20260112_002】发送通知:订单已完成,感谢您的购买 【订单ORDER_20260112_002】状态已从已发货→已完成 ===== 第四步:尝试在已完成状态下取消订单(非法操作) ===== 【订单ORDER_20260112_002】订单已完成,无法处理任何事件CancelOrder三、两种实现方式的对比与适用场景
| 实现方式 | 优点 | 缺点 | 适用场景 |
| 简单状态机(Dictionary + 委托) | 实现简单、轻量级、无需额外类结构、开发效率高 | 扩展性差(新增状态需修改字典配置和转移逻辑)、复杂场景下逻辑易混乱 | 状态数量少(5 个以内)、转移规则简单、无需频繁扩展的场景 |
| 面向对象状态机(状态模式) | 扩展性强(新增状态只需新增类)、逻辑内聚、可读性高、符合开闭原则 | 实现复杂、类数量多、有一定学习成本 | 状态数量多、转移规则复杂、需要频繁扩展(新增状态 / 事件)的企业级场景(如电商、物流、工作流) |
四、C# 中的高级状态机
对于超复杂的状态机场景(如带状态历史、并发状态、子状态机),无需重复造轮子,可以使用成熟的第三方库:
- Stateless:轻量级、开源的.NET 状态机库,支持流畅 API、委托、异步动作、状态持久化等,NuGet 安装:
Install-Package Stateless - Automatonymous:基于 MassTransit 的事件驱动状态机库,支持复杂的异步流转和分布式场景,NuGet 安装:
Install-Package Automatonymous
总结
- 状态机是描述对象有限状态、转移规则、事件和动作的模型,核心价值是解决复杂状态切换的逻辑混乱;
- 状态机的 4 个核心要素:状态(State)、事件(Event)、转移(Transition)、动作(Action);
- C# 中简单场景可使用
Dictionary+委托实现轻量级状态机,复杂场景推荐基于状态模式的面向对象实现; - 状态机的核心思想是 “状态驱动行为”,通过明确的转移规则减少非法状态,提高代码的可维护性和扩展性。