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