From a5daadea966b003b51abef4cc8d72394de9f8e0b Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 15 Feb 2026 18:23:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(state):=20=E6=B7=BB=E5=8A=A0=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9C=BA=E5=BC=82=E6=AD=A5=E6=93=8D=E4=BD=9C=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了异步注销状态功能 UnregisterAsync - 添加了异步状态切换检查 CanChangeToAsync - 实现了异步状态切换功能 ChangeToAsync - 添加了异步回退到上一状态 GoBackAsync - 支持同步状态(IState)和异步状态(IAsyncState)的统一管理 - 提供了异步状态转换过程中的回调机制 - 完善了异步状态切换的核心逻辑处理 - [release ci] --- .../state/IStateMachine.cs | 27 ++ GFramework.Core/state/StateMachine.cs | 235 ++++++++++++++++++ 2 files changed, 262 insertions(+) diff --git a/GFramework.Core.Abstractions/state/IStateMachine.cs b/GFramework.Core.Abstractions/state/IStateMachine.cs index 4c4b5c3..fa3c130 100644 --- a/GFramework.Core.Abstractions/state/IStateMachine.cs +++ b/GFramework.Core.Abstractions/state/IStateMachine.cs @@ -2,6 +2,7 @@ namespace GFramework.Core.Abstractions.state; /// /// 状态机接口,用于管理状态的注册、切换和验证 +/// 支持同步和异步状态操作 /// public interface IStateMachine { @@ -22,6 +23,12 @@ public interface IStateMachine /// 要注销的状态类型,必须实现IState接口 IStateMachine Unregister() where T : IState; + /// + /// 异步从状态机中注销指定类型的状态 + /// + /// 要注销的状态类型,必须实现IState接口 + Task UnregisterAsync() where T : IState; + /// /// 检查是否可以切换到指定类型的状态 /// @@ -29,6 +36,13 @@ public interface IStateMachine /// 如果可以切换则返回true,否则返回false bool CanChangeTo() where T : IState; + /// + /// 异步检查是否可以切换到指定类型的状态 + /// + /// 目标状态类型,必须实现IState接口 + /// 如果可以切换则返回true,否则返回false + Task CanChangeToAsync() where T : IState; + /// /// 切换到指定类型的状态 /// @@ -36,6 +50,13 @@ public interface IStateMachine /// 如果成功切换则返回true,否则返回false bool ChangeTo() where T : IState; + /// + /// 异步切换到指定类型的状态 + /// + /// 要切换到的状态类型,必须实现IState接口 + /// 如果成功切换则返回true,否则返回false + Task ChangeToAsync() where T : IState; + /// /// 检查指定类型的状态是否已注册 /// @@ -74,6 +95,12 @@ public interface IStateMachine /// 如果成功回退则返回true,否则返回false bool GoBack(); + /// + /// 异步回退到上一个状态 + /// + /// 如果成功回退则返回true,否则返回false + Task GoBackAsync(); + /// /// 清空状态历史记录 /// diff --git a/GFramework.Core/state/StateMachine.cs b/GFramework.Core/state/StateMachine.cs index a97cc61..5b7a372 100644 --- a/GFramework.Core/state/StateMachine.cs +++ b/GFramework.Core/state/StateMachine.cs @@ -4,6 +4,7 @@ namespace GFramework.Core.state; /// /// 状态机实现类,用于管理状态的注册、切换和生命周期 +/// 同时支持同步状态(IState)和异步状态(IAsyncState) /// public class StateMachine(int maxHistorySize = 10) : IStateMachine { @@ -63,6 +64,45 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine return this; } + /// + /// 异步注销指定类型的状态 + /// + /// 要注销的状态类型 + public async Task UnregisterAsync() where T : IState + { + IState? stateToUnregister; + + lock (_lock) + { + var type = typeof(T); + if (!States.TryGetValue(type, out stateToUnregister)) return this; + } + + // 如果当前状态是要注销的状态,则先执行退出逻辑 + if (Current == stateToUnregister) + { + if (Current is IAsyncState asyncState) + await asyncState.OnExitAsync(null); + else + Current.OnExit(null); + + Current = null; + } + + lock (_lock) + { + // 从历史记录中移除该状态的所有引用 + var tempStack = new Stack(_stateHistory.Reverse()); + _stateHistory.Clear(); + foreach (var historyState in tempStack.Where(s => s != stateToUnregister)) + _stateHistory.Push(historyState); + + States.Remove(typeof(T)); + } + + return this; + } + /// /// 检查是否可以切换到指定类型的状态 /// @@ -76,6 +116,26 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine return Current?.CanTransitionTo(target) ?? true; } + /// + /// 异步检查是否可以切换到指定类型的状态 + /// + /// 目标状态类型 + /// 如果可以切换则返回true,否则返回false + public async Task CanChangeToAsync() where T : IState + { + if (!States.TryGetValue(typeof(T), out var target)) + return false; + + if (Current == null) return true; + + // 如果当前状态是异步状态,使用异步方法 + if (Current is IAsyncState asyncState) + return await asyncState.CanTransitionToAsync(target); + + // 否则使用同步方法 + return Current.CanTransitionTo(target); + } + /// /// 切换到指定类型的状态 /// @@ -102,6 +162,43 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine } } + /// + /// 异步切换到指定类型的状态 + /// + /// 目标状态类型 + /// 如果成功切换则返回true,否则返回false + /// 当目标状态未注册时抛出 + public async Task ChangeToAsync() where T : IState + { + IState target; + + lock (_lock) + { + // 检查目标状态是否已注册 + if (!States.TryGetValue(typeof(T), out target!)) + throw new InvalidOperationException("State not registered."); + } + + // 验证当前状态是否可以转换到目标状态(异步) + if (Current != null) + { + bool canTransition; + if (Current is IAsyncState asyncState) + canTransition = await asyncState.CanTransitionToAsync(target); + else + canTransition = Current.CanTransitionTo(target); + + if (!canTransition) + { + await OnTransitionRejectedAsync(Current, target); + return false; + } + } + + await ChangeInternalAsync(target); + return true; + } + /// /// 检查指定类型的状态是否已注册 /// @@ -178,6 +275,38 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine } } + /// + /// 异步回退到上一个状态 + /// + /// 如果成功回退则返回true,否则返回false + public async Task GoBackAsync() + { + IState? previousState = null; + + // 循环查找有效的历史状态 + while (previousState == null) + { + lock (_lock) + { + if (_stateHistory.Count == 0) + return false; + + var candidate = _stateHistory.Pop(); + + // 检查状态是否仍然注册 + if (States.ContainsValue(candidate)) + { + previousState = candidate; + } + // 如果状态已被注销,继续循环尝试更早的状态 + } + } + + // 回退时不添加到历史记录 + await ChangeInternalWithoutHistoryAsync(previousState); + return true; + } + /// /// 清空状态历史记录 /// @@ -207,6 +336,32 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine OnStateChanged(old, Current); } + /// + /// 异步内部状态切换方法(不记录历史),用于回退操作 + /// + /// 下一个状态实例 + protected virtual async Task ChangeInternalWithoutHistoryAsync(IState next) + { + if (Current == next) return; + + var old = Current; + await OnStateChangingAsync(old, next); + + if (old is IAsyncState asyncOld) + await asyncOld.OnExitAsync(next); + else + old?.OnExit(next); + + Current = next; + + if (Current is IAsyncState asyncCurrent) + await asyncCurrent.OnEnterAsync(old); + else + Current.OnEnter(old); + + await OnStateChangedAsync(old, Current); + } + /// /// 内部状态切换方法,处理状态切换的核心逻辑 /// @@ -248,6 +403,53 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine OnStateChanged(old, Current); } + /// + /// 异步内部状态切换方法,处理状态切换的核心逻辑 + /// + /// 下一个状态实例 + protected virtual async Task ChangeInternalAsync(IState next) + { + // 检查是否为相同状态,避免不必要的切换 + if (Current == next) return; + + var old = Current; + await OnStateChangingAsync(old, next); + + // 将当前状态添加到历史记录 + lock (_lock) + { + if (Current != null) + { + _stateHistory.Push(Current); + + // 限制历史记录大小 + if (_stateHistory.Count > maxHistorySize) + { + // 移除最旧的记录(栈底元素) + var tempStack = new Stack(_stateHistory.Reverse().Skip(1)); + _stateHistory.Clear(); + foreach (var state in tempStack.Reverse()) _stateHistory.Push(state); + } + } + } + + // 执行退出逻辑(异步或同步) + if (old is IAsyncState asyncOld) + await asyncOld.OnExitAsync(next); + else + old?.OnExit(next); + + Current = next; + + // 执行进入逻辑(异步或同步) + if (Current is IAsyncState asyncCurrent) + await asyncCurrent.OnEnterAsync(old); + else + Current.OnEnter(old); + + await OnStateChangedAsync(old, Current); + } + /// /// 当状态转换被拒绝时的回调方法 /// @@ -257,6 +459,17 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine { } + /// + /// 当状态转换被拒绝时的异步回调方法 + /// + /// 源状态 + /// 目标状态 + protected virtual Task OnTransitionRejectedAsync(IState from, IState to) + { + OnTransitionRejected(from, to); + return Task.CompletedTask; + } + /// /// 当状态即将发生改变时的回调方法 /// @@ -266,6 +479,17 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine { } + /// + /// 当状态即将发生改变时的异步回调方法 + /// + /// 源状态 + /// 目标状态 + protected virtual Task OnStateChangingAsync(IState? from, IState to) + { + OnStateChanging(from, to); + return Task.CompletedTask; + } + /// /// 当状态改变完成后的回调方法 /// @@ -274,4 +498,15 @@ public class StateMachine(int maxHistorySize = 10) : IStateMachine protected virtual void OnStateChanged(IState? from, IState? to) { } + + /// + /// 当状态改变完成后的异步回调方法 + /// + /// 源状态 + /// 目标状态 + protected virtual Task OnStateChangedAsync(IState? from, IState? to) + { + OnStateChanged(from, to); + return Task.CompletedTask; + } } \ No newline at end of file