C# 关于状态机的实现(案例版)

大部分的状态机都是有限状态机,某些业务环境,或者其他环境中,如果有状态机其实还是很方便的。比如,我是用在了单个客户的Socket通信上,未连接状态,我就等连接。已连接状态,就等待下一步指令状态。这样的,逻辑就会简化许多。

以前在C语言上,自己实现过状态机,但是,过去好久了。也想通过C#实现,看看是不是方便许多。.

状态机的实现

状态机接口对象

    /// <summary>
    /// 状态对象
    /// </summary>
    public interface IStateObject
    {
        /// <summary>
        /// 进入状态
        /// </summary>
        void EnterState();
        /// <summary>
        /// 离开状态
        /// </summary>
        void ExitState();
        /// <summary>
        /// 更新状态
        /// </summary>
        void UpdateState();
    }

也可以在 更新状态里,自己设置下一个状态。算是转到指定状态上。

比如登录成功

状态机核心逻辑

    /// <summary>
    /// 状态机
    /// </summary>
    public class StateMachine
    {
        /// <summary>
        /// 运行 Update 时间间隔 毫秒
        /// </summary>
        public int RunInterval = 500;
        /// <summary>
        /// 当前状态
        /// </summary>
        private string CurrentState;
        /// <summary>
        /// 字典存放当前所有对象
        /// </summary>
        private Dictionary<string, IStateObject> Dic = new();
        /// <summary>
        /// 当前的线程对象
        /// </summary>
        private Thread thread;
        /// <summary>
        /// 是否已经在运行
        /// </summary>
        private bool IsRun = false;
        public StateMachine(int runInterval = 500)
        {
            this.RunInterval = runInterval;
        }
        /// <summary>
        /// 注册一个状态对象
        /// </summary>
        /// <param name="stateObject"></param>
        /// <param name="istateObject"></param>
        public void Register(string stateObject, IStateObject istateObject)
        {
            Dic.TryAdd(stateObject, istateObject);
        }
        /// <summary>
        /// 注册一个状态对象
        /// </summary>
        /// <param name="stateObject"></param>
        /// <param name="istateObject"></param>
        public void Register(Dictionary<string, IStateObject> stateObjects)
        {
            if (stateObjects?.Any() == true)
            {
                foreach (var item in stateObjects)
                {
                    Dic.TryAdd(item.Key, item.Value);
                }
            }
        }
        /// <summary>
        /// 设置当前状态
        /// </summary>
        /// <param name="stateObject"></param>
        public void SetState(string stateObject)
        {
            if (CurrentState != stateObject)
            {
                if (CurrentState != null && Dic.TryGetValue(CurrentState, out var oldObj))
                {
                    oldObj.ExitState();
                }
                CurrentState = stateObject;
                if (CurrentState != null && Dic.TryGetValue(CurrentState, out var newObj))
                {
                    newObj.EnterState();
                }
            }
        }
        /// <summary>
        /// 自己启动服务
        /// </summary>
        public void Start()
        {
            if (!IsRun)
            {
                IsRun = true;
                thread = new Thread(new ThreadStart(Run));
                thread.IsBackground = true;
                thread.Start();
                Console.WriteLine("状态机启动");
            }
        }
        /// <summary>
        /// 自己停止服务
        /// </summary>
        public void Close()
        {
            if (IsRun)
            {
                //最后一个状态直接退出
                if (CurrentState != null && Dic.TryGetValue(CurrentState, out var oldObj))
                {
                    oldObj.ExitState();
                }
                IsRun = false;
                try
                {
                    thread.Interrupt();
                }
                catch (Exception)
                {
                }
                Thread.Sleep(50);
                thread = null;
                Console.WriteLine("状态机关闭");
            }
        }
        /// <summary>
        /// 线程执行的任务
        /// </summary>
        private void Run()
        {
            try
            {
                while (IsRun)
                {
                    Updata();
                    SpinWait.SpinUntil(() => !IsRun, RunInterval);
                }
            }
            catch (Exception) { };
        }
        /// <summary>
        /// 更新数据
        /// </summary>
        public void Updata()
        {
            if (CurrentState != null && Dic.TryGetValue(CurrentState, out var objobj))
            {
                objobj.UpdateState();
            }
        }
    }

这个是状态机的核心实现,大佬随便看一下应该就知道啥意思了。

定义两个状态对象

/// <summary>
/// 一只猫
/// </summary>
public class Cat : IStateObject 
{
    public void EnterState()
    {
        Console.WriteLine("小猫进来了");
    }

    public void ExitState()
    {
        Console.WriteLine("小猫出去了");
    }

    public void UpdateState()
    {
        Console.WriteLine("小猫在玩逗猫棒!");
    }
}
/// <summary>
/// 一只狗
/// </summary>
public class Dog : IStateObject 
{
    public void EnterState()
    {
        Console.WriteLine("小狗进来了");
    }

    public void ExitState()
    {
        Console.WriteLine("小狗出去了");
    }

    public void UpdateState()
    {
        Console.WriteLine("小狗在玩耍!");
    }
}

一只猫,一只狗,就可以切换状态效果了。

测试代码

static void Main(string[] args)
{
    StateMachine stateMachine = new StateMachine(1500);
    //状态机
    //根据当前的不同的状态,做出不同的事件操作
    stateMachine.Register(nameof(Cat), new Cat());
    stateMachine.Register(nameof(Dog), new Dog());
    //启动状态机
    stateMachine.Start();
    //开始执行状态机
    //设置当前状态
    stateMachine.SetState(nameof(Cat));
    Thread.Sleep(2000);
    stateMachine.SetState(nameof(Dog));
    Thread.Sleep(2000);
    stateMachine.SetState(nameof(Cat));
    Thread.Sleep(2000);
    //状态机停止
    stateMachine.Close();
    Console.WriteLine("状态机执行完毕!");
    Console.ReadLine();
}

运行结果

C# 关于状态机的实现(案例版)

总结

状态机C#实现完之后,用着还是挺方便的。对于有些流程也可以用状态机来实现。

代码地址

https://github.com/kesshei/StateMachineDemo.git

https://gitee.com/kesshei/StateMachineDemo.git