C#状态机

一、状态机的核心概念

1. 什么是状态机?

状态机(Finite State Machine, FSM,又称有限状态机) 是一种数学模型和编程思想,用于描述一个对象(或系统)在其生命周期内的有限个状态 ,以及这些状态之间的转移规则 、触发转移的事件 / 条件 ,还有状态转移过程中(或处于某个状态时)需要执行的动作 / 行为

简单理解:它就像对象的 "行为剧本",规定了对象在什么状态下,遇到什么事情,应该切换到什么新状态,同时要做什么事。

2. 状态机的核心组成要素

一个完整的有限状态机包含 4 个核心部分,缺一不可:

  • 状态(State):对象的离散状态,数量有限。例如 "电梯停止""电梯上行""电梯下行";"订单待支付""订单已支付""订单已取消"。
  • 事件(Event/Trigger):触发状态转移的外部或内部条件 / 信号。例如 "乘客按下 5 楼按钮"(触发电梯上行)、"用户完成支付"(触发订单从待支付→已支付)。
  • 转移(Transition):从一个状态切换到另一个状态的规则,通常是 "当前状态 + 事件→目标状态"。例如 "电梯停止(当前状态)+ 按下 5 楼按钮(事件)→ 电梯上行(目标状态)"。
  • 动作(Action):在状态转移过程中(或进入 / 退出某个状态时)执行的逻辑。例如 "电梯切换到上行状态时,启动电机并播报'前往 5 楼'"、"订单变为已支付时,生成物流单号并发送通知"。

3. 状态机的核心价值(解决什么问题)

在编程中,状态机主要解决复杂状态切换的逻辑混乱问题,具体价值体现在:

  • 消除大量嵌套if/elseswitch/case,让状态相关逻辑更清晰、更易维护;
  • 明确状态转移规则,减少 "非法状态" 出现的概率(例如订单已取消后,无法再触发支付);
  • 状态与行为解耦,便于扩展新状态和新转移规则(符合开闭原则);
  • 逻辑可追溯性强,每个状态的行为和转移都有明确定义,便于调试。

二、C# 中状态机的两种实现方式

下面以电商订单状态流转为场景(最经典的状态机应用场景之一),讲解两种常用实现方式。

场景定义(统一约束)

先明确订单的核心状态、事件和转移规则:

|--------------|-------------|----------------|-----------------|
| 核心状态(State) | 触发事件(Event) | 目标状态 | 执行动作(Action) |
| 待支付(Pending) | 用户支付成功 | 已支付(Paid) | 生成物流单号、发送支付成功通知 |
| 待支付(Pending) | 用户取消订单 | 已取消(Cancelled) | 释放库存、发送取消通知 |
| 已支付(Paid) | 商家发货 | 已发货(Shipped) | 更新物流信息、发送发货通知 |
| 已发货(Shipped) | 用户确认收货 | 已完成(Completed) | 完成订单结算、关闭交易 |

前置准备:定义枚举(状态和事件)

首先用 C# 枚举明确订单的状态和触发事件,提高代码可读性和类型安全性:

cs 复制代码
using System;
using System.Collections.Generic;

// 订单状态枚举
public enum OrderState
{
    Pending,    // 待支付
    Paid,       // 已支付
    Shipped,    // 已发货
    Completed,  // 已完成
    Cancelled   // 已取消
}

// 订单事件枚举(触发状态转移的信号)
public enum OrderEvent
{
    PaySuccess,  // 支付成功
    CancelOrder, // 取消订单
    ShipGoods,   // 商家发货
    ConfirmReceipt // 用户确认收货
}

方式 1:简单状态机(基于Dictionary+ 委托,适合简单场景)

这种方式无需复杂的类结构,通过字典存储 "当前状态 + 事件→转移规则",配合委托封装动作,实现轻量级状态机。

cs 复制代码
/// <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
}

测试代码(使用状态机)

cs 复制代码
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();
    }
}

运行结果

cs 复制代码
===== 第一步:用户支付订单 =====
【订单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:定义状态接口(统一所有状态的行为)

cs 复制代码
/// <summary>
/// 订单状态接口(统一所有订单状态的行为)
/// </summary>
public interface IOrderState
{
    /// <summary>
    /// 处理当前状态下的事件,驱动状态转移
    /// </summary>
    /// <param name="orderContext">订单上下文(持有状态机的核心信息)</param>
    /// <param name="event">触发事件</param>
    void Handle(OrderContext orderContext, OrderEvent @event);
}

步骤 2:定义订单上下文(封装状态机的公共信息和状态切换)

cs 复制代码
/// <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:实现各个具体状态类(内聚对应状态的逻辑)

cs 复制代码
#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:测试面向对象状态机

cs 复制代码
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();
    }
}

运行结果(与简单状态机一致,扩展性更强)

cs 复制代码
===== 第一步:用户支付订单 =====
【订单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

总结

  1. 状态机是描述对象有限状态、转移规则、事件和动作的模型,核心价值是解决复杂状态切换的逻辑混乱;
  2. 状态机的 4 个核心要素:状态(State)、事件(Event)、转移(Transition)、动作(Action);
  3. C# 中简单场景可使用Dictionary+委托实现轻量级状态机,复杂场景推荐基于状态模式的面向对象实现;
  4. 状态机的核心思想是 "状态驱动行为",通过明确的转移规则减少非法状态,提高代码的可维护性和扩展性。
相关推荐
办公自动化软件定制化开发python2 小时前
基于PyQt5开发的文件智能查找工具,开源思路+完整实现,解决办公文件检索痛点
开发语言·qt
古城小栈2 小时前
Tokio:Rust 异步界的 “霸主”
开发语言·后端·rust
_OP_CHEN2 小时前
【从零开始的Qt开发指南】(二十)Qt 多线程深度实战指南:从基础 API 到线程安全,带你实现高效并发应用
开发语言·c++·qt·安全·线程·前端开发·线程安全
开开心心_Every2 小时前
离线黑白照片上色工具:操作简单效果逼真
java·服务器·前端·学习·edge·c#·powerpoint
爱喝水的鱼丶2 小时前
SAP-ABAP:SAP性能侦探:STAD事务码的深度解析与应用实战
开发语言·数据库·学习·sap·abap
while(1){yan}2 小时前
SpringAOP
java·开发语言·spring boot·spring·aop
专注于大数据技术栈2 小时前
java学习--Collection
java·开发语言·学习
techdashen2 小时前
Go 1.18+ slice 扩容机制详解
开发语言·后端·golang
浮游本尊2 小时前
React 18.x 学习计划 - 第十二天:企业级实践与进阶主题
学习·react.js·状态模式