diff --git a/GFramework.Core.Tests/state/StateMachineSystemTests.cs b/GFramework.Core.Tests/state/StateMachineSystemTests.cs
index cb9facc..6e8a75f 100644
--- a/GFramework.Core.Tests/state/StateMachineSystemTests.cs
+++ b/GFramework.Core.Tests/state/StateMachineSystemTests.cs
@@ -270,6 +270,93 @@ public class StateMachineSystemTests
_stateMachine.Destroy();
}
+
+ ///
+ /// 测试异步ChangeToAsync发送StateChangedEvent事件
+ /// 验证当状态机使用异步方法切换到新状态时,会正确触发StateChangedEvent事件
+ ///
+ [Test]
+ public async Task ChangeToAsync_Should_Send_StateChangedEvent()
+ {
+ var eventReceived = false;
+ StateChangedEvent? receivedEvent = null;
+
+ _eventBus!.Register(e =>
+ {
+ eventReceived = true;
+ receivedEvent = e;
+ });
+
+ var state1 = new TestStateV5();
+ var state2 = new TestStateV5();
+
+ _stateMachine!.Register(state1);
+ _stateMachine.Register(state2);
+
+ _stateMachine.Init();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(eventReceived, Is.True);
+ Assert.That(receivedEvent!.OldState, Is.Null);
+ Assert.That(receivedEvent.NewState, Is.InstanceOf());
+ }
+
+ ///
+ /// 测试异步ChangeToAsync发送StateChangedEvent事件(包含旧状态)
+ /// 验证当状态机使用异步方法从一个状态切换到另一个状态时,会正确触发StateChangedEvent事件
+ ///
+ [Test]
+ public async Task ChangeToAsync_Should_Send_StateChangedEvent_With_OldState()
+ {
+ var eventReceived = false;
+ StateChangedEvent? receivedEvent = null;
+
+ _eventBus!.Register(e =>
+ {
+ eventReceived = true;
+ receivedEvent = e;
+ });
+
+ var state1 = new TestStateV5();
+ var state2 = new TestStateV5_2();
+
+ _stateMachine!.Register(state1);
+ _stateMachine.Register(state2);
+
+ _stateMachine.Init();
+ await _stateMachine.ChangeToAsync();
+
+ eventReceived = false;
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(eventReceived, Is.True);
+ Assert.That(receivedEvent!.OldState, Is.InstanceOf());
+ Assert.That(receivedEvent.NewState, Is.InstanceOf());
+ }
+
+ ///
+ /// 测试异步切换时多次状态变更都能正确触发事件
+ ///
+ [Test]
+ public async Task ChangeToAsync_MultipleChanges_Should_Send_Events_Correctly()
+ {
+ var eventCount = 0;
+
+ _eventBus!.Register(_ => { eventCount++; });
+
+ var state1 = new TestStateV5();
+ var state2 = new TestStateV5_2();
+
+ _stateMachine!.Register(state1);
+ _stateMachine.Register(state2);
+
+ _stateMachine.Init();
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(eventCount, Is.EqualTo(3));
+ }
}
#region Test Classes
@@ -370,4 +457,41 @@ public class TestStateV5 : IState
}
}
+///
+/// 第二个测试用的普通状态实现,用于区分不同状态类型
+///
+public class TestStateV5_2 : IState
+{
+ ///
+ /// 状态标识符
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 检查是否可以转换到指定状态
+ ///
+ /// 目标状态
+ /// 始终返回true表示允许转换
+ public bool CanTransitionTo(IState next)
+ {
+ return true;
+ }
+
+ ///
+ /// 进入状态时调用
+ ///
+ /// 前一个状态
+ public void OnEnter(IState? previous)
+ {
+ }
+
+ ///
+ /// 退出状态时调用
+ ///
+ /// 下一个状态
+ public void OnExit(IState? next)
+ {
+ }
+}
+
#endregion
\ No newline at end of file
diff --git a/GFramework.Core.Tests/state/StateMachineTests.cs b/GFramework.Core.Tests/state/StateMachineTests.cs
index 59485ae..7542ac6 100644
--- a/GFramework.Core.Tests/state/StateMachineTests.cs
+++ b/GFramework.Core.Tests/state/StateMachineTests.cs
@@ -241,6 +241,394 @@ public class StateMachineTests
Assert.That(_stateMachine.Current, Is.SameAs(state1));
}
+ ///
+ /// 验证异步注销后状态应从字典中移除
+ ///
+ [Test]
+ public async Task UnregisterAsync_Should_RemoveState_FromDictionary()
+ {
+ var state = new TestStateV2();
+ _stateMachine.Register(state);
+ await _stateMachine.UnregisterAsync();
+
+ Assert.That(_stateMachine.ContainsState(), Is.False);
+ }
+
+ ///
+ /// 验证异步注销活动同步状态时调用OnExit
+ ///
+ [Test]
+ public async Task UnregisterAsync_WhenStateIsActive_WithSyncState_Should_Invoke_OnExit()
+ {
+ var state = new TestStateV2();
+ _stateMachine.Register(state);
+ _stateMachine.ChangeTo();
+ await _stateMachine.UnregisterAsync();
+
+ Assert.That(state.ExitCalled, Is.True);
+ Assert.That(state.ExitTo, Is.Null);
+ Assert.That(_stateMachine.Current, Is.Null);
+ }
+
+ ///
+ /// 验证异步注销活动异步状态时调用OnExitAsync
+ ///
+ [Test]
+ public async Task UnregisterAsync_WhenStateIsActive_WithAsyncState_Should_Invoke_OnExitAsync()
+ {
+ var state = new TestAsyncState();
+ _stateMachine.Register(state);
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.UnregisterAsync();
+
+ Assert.That(state.ExitCalled, Is.True);
+ Assert.That(state.ExitTo, Is.Null);
+ Assert.That(_stateMachine.Current, Is.Null);
+ }
+
+ ///
+ /// 验证异步注销非活动状态不调用退出方法
+ ///
+ [Test]
+ public async Task UnregisterAsync_WhenStateNotActive_Should_Not_Invoke_Exit()
+ {
+ var state1 = new TestStateV2();
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ _stateMachine.ChangeTo();
+
+ await _stateMachine.UnregisterAsync();
+
+ Assert.That(state1.ExitCalled, Is.False);
+ Assert.That(_stateMachine.Current, Is.SameAs(state1));
+ }
+
+ ///
+ /// 验证异步切换检查未注册状态返回false
+ ///
+ [Test]
+ public async Task CanChangeToAsync_WhenStateNotRegistered_Should_ReturnFalse()
+ {
+ var result = await _stateMachine.CanChangeToAsync();
+ Assert.That(result, Is.False);
+ }
+
+ ///
+ /// 验证异步切换检查已注册状态返回true
+ ///
+ [Test]
+ public async Task CanChangeToAsync_WhenStateRegistered_Should_ReturnTrue()
+ {
+ var state = new TestStateV2();
+ _stateMachine.Register(state);
+
+ var result = await _stateMachine.CanChangeToAsync();
+ Assert.That(result, Is.True);
+ }
+
+ ///
+ /// 验证异步切换检查当前状态拒绝转换时返回false
+ ///
+ [Test]
+ public async Task CanChangeToAsync_WhenCurrentStateDeniesTransition_Should_ReturnFalse()
+ {
+ var state1 = new TestStateV2 { AllowTransition = false };
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ _stateMachine.ChangeTo();
+
+ var result = await _stateMachine.CanChangeToAsync();
+ Assert.That(result, Is.False);
+ }
+
+ ///
+ /// 验证异步切换检查使用异步状态时调用CanTransitionToAsync
+ ///
+ [Test]
+ public async Task CanChangeToAsync_WithAsyncState_Should_Call_CanTransitionToAsync()
+ {
+ var state1 = new TestAsyncState();
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ await _stateMachine.ChangeToAsync();
+
+ await _stateMachine.CanChangeToAsync();
+ Assert.That(state1.CanTransitionToCallCount, Is.EqualTo(1));
+ }
+
+ ///
+ /// 验证异步切换检查使用同步状态时调用CanTransitionTo
+ ///
+ [Test]
+ public async Task CanChangeToAsync_WithSyncState_Should_Call_CanTransitionTo()
+ {
+ var state1 = new TestStateV2 { AllowTransition = false };
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ _stateMachine.ChangeTo();
+
+ var result = await _stateMachine.CanChangeToAsync();
+ Assert.That(result, Is.False);
+ }
+
+ ///
+ /// 验证异步状态切换能够正确设置当前状态
+ ///
+ [Test]
+ public async Task ChangeToAsync_Should_SetCurrentState()
+ {
+ var state = new TestStateV2();
+ _stateMachine.Register(state);
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(_stateMachine.Current, Is.SameAs(state));
+ }
+
+ ///
+ /// 验证异步状态切换对于异步状态调用OnEnterAsync
+ ///
+ [Test]
+ public async Task ChangeToAsync_Should_Invoke_OnEnterAsync_ForAsyncState()
+ {
+ var state = new TestAsyncState();
+ _stateMachine.Register(state);
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state.EnterCalled, Is.True);
+ Assert.That(state.EnterFrom, Is.Null);
+ }
+
+ ///
+ /// 验证异步状态切换对于同步状态调用OnEnter
+ ///
+ [Test]
+ public async Task ChangeToAsync_Should_Invoke_OnEnter_ForSyncState()
+ {
+ var state = new TestStateV2();
+ _stateMachine.Register(state);
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state.EnterCalled, Is.True);
+ Assert.That(state.EnterFrom, Is.Null);
+ }
+
+ ///
+ /// 验证异步状态切换当存在当前异步状态时调用OnExitAsync
+ ///
+ [Test]
+ public async Task ChangeToAsync_When_CurrentStateExists_WithAsyncState_Should_Invoke_OnExitAsync()
+ {
+ var state1 = new TestAsyncState();
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state1.ExitCalled, Is.True);
+ Assert.That(state1.ExitTo, Is.SameAs(state2));
+ }
+
+ ///
+ /// 验证异步状态切换当存在当前同步状态时调用OnExit
+ ///
+ [Test]
+ public async Task ChangeToAsync_When_CurrentStateExists_WithSyncState_Should_Invoke_OnExit()
+ {
+ var state1 = new TestStateV2();
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state1.ExitCalled, Is.True);
+ Assert.That(state1.ExitTo, Is.SameAs(state2));
+ }
+
+ ///
+ /// 验证异步状态切换到相同状态时不应调用回调方法
+ ///
+ [Test]
+ public async Task ChangeToAsync_ToSameState_Should_NotInvoke_Callbacks()
+ {
+ var state = new TestStateV2();
+ _stateMachine.Register(state);
+ await _stateMachine.ChangeToAsync();
+
+ var enterCount = state.EnterCallCount;
+ var exitCount = state.ExitCallCount;
+
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state.EnterCallCount, Is.EqualTo(enterCount));
+ Assert.That(state.ExitCallCount, Is.EqualTo(exitCount));
+ }
+
+ ///
+ /// 验证异步状态切换到未注册状态时应抛出InvalidOperationException
+ ///
+ [Test]
+ public void ChangeToAsync_ToUnregisteredState_Should_ThrowInvalidOperationException()
+ {
+ Assert.ThrowsAsync(async () => await _stateMachine.ChangeToAsync());
+ }
+
+ ///
+ /// 验证异步状态切换当当前状态拒绝转换时不应发生状态变化
+ ///
+ [Test]
+ public async Task ChangeToAsync_WhenCurrentStateDeniesTransition_Should_NotChange()
+ {
+ var state1 = new TestStateV2 { AllowTransition = false };
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ await _stateMachine.ChangeToAsync();
+
+ var oldState = _stateMachine.Current;
+ var result = await _stateMachine.ChangeToAsync();
+
+ Assert.That(result, Is.False);
+ Assert.That(_stateMachine.Current, Is.SameAs(oldState));
+ Assert.That(_stateMachine.Current, Is.SameAs(state1));
+ Assert.That(state2.EnterCalled, Is.False);
+ }
+
+ ///
+ /// 验证异步状态切换当当前异步状态拒绝转换时调用CanTransitionToAsync
+ ///
+ [Test]
+ public async Task ChangeToAsync_WhenCurrentStateDeniesTransition_WithAsyncState_Should_Call_CanTransitionToAsync()
+ {
+ var state1 = new TestAsyncState { AllowTransition = false };
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ await _stateMachine.ChangeToAsync();
+
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state1.CanTransitionToCallCount, Is.EqualTo(1));
+ }
+
+ ///
+ /// 验证异步回退能够返回到上一个状态
+ ///
+ [Test]
+ public async Task GoBackAsync_Should_ReturnTo_PreviousState()
+ {
+ var state1 = new TestStateV2();
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+ var result = await _stateMachine.GoBackAsync();
+
+ Assert.That(result, Is.True);
+ Assert.That(_stateMachine.Current, Is.SameAs(state1));
+ }
+
+ ///
+ /// 验证异步回退当没有历史记录时返回false
+ ///
+ [Test]
+ public async Task GoBackAsync_WhenNoHistory_Should_ReturnFalse()
+ {
+ var result = await _stateMachine.GoBackAsync();
+ Assert.That(result, Is.False);
+ }
+
+ ///
+ /// 验证异步回退调用正确的状态转换方法
+ ///
+ [Test]
+ public async Task GoBackAsync_Should_Invoke_Correct_Transition_Methods()
+ {
+ var state1 = new TestAsyncState();
+ var state2 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.GoBackAsync();
+
+ Assert.That(state2.ExitCalled, Is.True);
+ Assert.That(state1.EnterCallCount, Is.EqualTo(2));
+ }
+
+ ///
+ /// 验证从同步状态切换到异步状态能够正常工作
+ ///
+ [Test]
+ public async Task ChangeToAsync_FromSyncToAsyncState_Should_Work()
+ {
+ var state1 = new TestStateV2();
+ var state2 = new TestAsyncState();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state1.ExitCalled, Is.True);
+ Assert.That(state2.EnterCalled, Is.True);
+ Assert.That(_stateMachine.Current, Is.SameAs(state2));
+ }
+
+ ///
+ /// 验证从异步状态切换到同步状态能够正常工作
+ ///
+ [Test]
+ public async Task ChangeToAsync_FromAsyncToSyncState_Should_Work()
+ {
+ var state1 = new TestAsyncState();
+ var state2 = new TestStateV2();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state1.ExitCalled, Is.True);
+ Assert.That(state2.EnterCalled, Is.True);
+ Assert.That(_stateMachine.Current, Is.SameAs(state2));
+ }
+
+ ///
+ /// 验证多次异步状态转换应正确调用回调方法
+ ///
+ [Test]
+ public async Task MultipleAsyncStateChanges_Should_Invoke_Callbacks_Correctly()
+ {
+ var state1 = new TestAsyncState();
+ var state2 = new TestStateV2();
+ var state3 = new TestStateV3();
+ _stateMachine.Register(state1);
+ _stateMachine.Register(state2);
+ _stateMachine.Register(state3);
+
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+ await _stateMachine.ChangeToAsync();
+
+ Assert.That(state1.EnterCalled, Is.True);
+ Assert.That(state1.ExitCalled, Is.True);
+ Assert.That(state2.EnterCalled, Is.True);
+ Assert.That(state2.ExitCalled, Is.True);
+ Assert.That(state3.EnterCalled, Is.True);
+ Assert.That(state3.ExitCalled, Is.False);
+ }
+
///
/// 验证多次状态转换应正确调用回调方法
///
@@ -424,6 +812,85 @@ public sealed class TestStateV4 : IState
}
}
+///
+/// 异步测试状态类,同时实现IState和IAsyncState接口用于测试异步状态功能
+///
+public sealed class TestAsyncState : IState, IAsyncState
+{
+ public bool AllowTransition { get; set; } = true;
+ public bool EnterCalled { get; private set; }
+ public bool ExitCalled { get; private set; }
+ public int EnterCallCount { get; private set; }
+ public int ExitCallCount { get; private set; }
+ public IState? EnterFrom { get; private set; }
+ public IState? ExitTo { get; private set; }
+ public int CanTransitionToCallCount { get; private set; }
+
+ ///
+ /// 异步进入状态时的回调方法
+ ///
+ /// 从哪个状态进入
+ public async Task OnEnterAsync(IState? from)
+ {
+ await Task.Delay(1);
+ EnterCalled = true;
+ EnterCallCount++;
+ EnterFrom = from;
+ }
+
+ ///
+ /// 异步离开状态时的回调方法
+ ///
+ /// 离开到哪个状态
+ public async Task OnExitAsync(IState? to)
+ {
+ await Task.Delay(1);
+ ExitCalled = true;
+ ExitCallCount++;
+ ExitTo = to;
+ }
+
+ ///
+ /// 异步判断是否可以转换到目标状态
+ ///
+ /// 目标状态
+ /// 是否允许转换
+ public async Task CanTransitionToAsync(IState target)
+ {
+ await Task.Delay(1);
+ CanTransitionToCallCount++;
+ return AllowTransition;
+ }
+
+ ///
+ /// 进入状态时的回调方法(同步版本,抛出异常表示不应被调用)
+ ///
+ /// 从哪个状态进入
+ public void OnEnter(IState? from)
+ {
+ throw new InvalidOperationException("Sync OnEnter should not be called for async state");
+ }
+
+ ///
+ /// 离开状态时的回调方法(同步版本,抛出异常表示不应被调用)
+ ///
+ /// 离开到哪个状态
+ public void OnExit(IState? to)
+ {
+ throw new InvalidOperationException("Sync OnExit should not be called for async state");
+ }
+
+ ///
+ /// 判断是否可以转换到目标状态(同步版本,抛出异常表示不应被调用)
+ ///
+ /// 目标状态
+ /// 是否允许转换
+ public bool CanTransitionTo(IState target)
+ {
+ throw new InvalidOperationException("Sync CanTransitionTo should not be called for async state");
+ }
+}
+
///
/// 状态机扩展方法类
///
diff --git a/GFramework.Core.Tests/state/StateTests.cs b/GFramework.Core.Tests/state/StateTests.cs
index 850e59b..36f5154 100644
--- a/GFramework.Core.Tests/state/StateTests.cs
+++ b/GFramework.Core.Tests/state/StateTests.cs
@@ -194,6 +194,137 @@ public class StateTests
Assert.That(state2.EnterFrom, Is.SameAs(state1));
Assert.That(state3.EnterFrom, Is.SameAs(state2));
}
+
+ ///
+ /// 验证异步状态类是否正确实现了IAsyncState接口
+ ///
+ [Test]
+ public void AsyncState_Should_Implement_IAsyncState_Interface()
+ {
+ var state = new ConcreteAsyncStateV2();
+
+ Assert.That(state is IAsyncState);
+ }
+
+ ///
+ /// 验证进入异步状态时OnEnterAsync方法被正确调用并记录来源状态
+ ///
+ [Test]
+ public async Task OnEnterAsync_Should_BeCalled_When_AsyncState_Enters()
+ {
+ var state = new ConcreteAsyncStateV2();
+ var otherState = new ConcreteStateV3();
+
+ await state.OnEnterAsync(otherState);
+
+ Assert.That(state.EnterCalled, Is.True);
+ Assert.That(state.EnterFrom, Is.SameAs(otherState));
+ }
+
+ ///
+ /// 验证当传入null作为来源状态时异步状态的处理情况
+ ///
+ [Test]
+ public async Task OnEnterAsync_WithNull_Should_Set_EnterFrom_ToNull()
+ {
+ var state = new ConcreteAsyncStateV2();
+
+ await state.OnEnterAsync(null);
+
+ Assert.That(state.EnterCalled, Is.True);
+ Assert.That(state.EnterFrom, Is.Null);
+ }
+
+ ///
+ /// 验证退出异步状态时OnExitAsync方法被正确调用并记录目标状态
+ ///
+ [Test]
+ public async Task OnExitAsync_Should_BeCalled_When_AsyncState_Exits()
+ {
+ var state = new ConcreteAsyncStateV2();
+ var otherState = new ConcreteStateV3();
+
+ await state.OnExitAsync(otherState);
+
+ Assert.That(state.ExitCalled, Is.True);
+ Assert.That(state.ExitTo, Is.SameAs(otherState));
+ }
+
+ ///
+ /// 验证当传入null作为目标状态时异步状态的处理情况
+ ///
+ [Test]
+ public async Task OnExitAsync_WithNull_Should_Set_ExitTo_ToNull()
+ {
+ var state = new ConcreteAsyncStateV2();
+
+ await state.OnExitAsync(null);
+
+ Assert.That(state.ExitCalled, Is.True);
+ Assert.That(state.ExitTo, Is.Null);
+ }
+
+ ///
+ /// 验证允许转换时CanTransitionToAsync方法返回true
+ ///
+ [Test]
+ public async Task CanTransitionToAsync_WithAllowTrue_Should_ReturnTrue()
+ {
+ var state = new ConcreteAsyncStateV2 { AllowTransitions = true };
+ var target = new ConcreteStateV3();
+
+ var result = await state.CanTransitionToAsync(target);
+
+ Assert.That(result, Is.True);
+ }
+
+ ///
+ /// 验证不允许转换时CanTransitionToAsync方法返回false
+ ///
+ [Test]
+ public async Task CanTransitionToAsync_WithAllowFalse_Should_ReturnFalse()
+ {
+ var state = new ConcreteAsyncStateV2 { AllowTransitions = false };
+ var target = new ConcreteStateV3();
+
+ var result = await state.CanTransitionToAsync(target);
+
+ Assert.That(result, Is.False);
+ }
+
+ ///
+ /// 验证CanTransitionToAsync方法正确接收目标状态参数
+ ///
+ [Test]
+ public async Task CanTransitionToAsync_Should_Receive_TargetState()
+ {
+ var state = new ConcreteAsyncStateV2 { AllowTransitions = true };
+ var target = new ConcreteStateV3();
+ IState? receivedTarget = null;
+
+ state.CanTransitionToAsyncAction = s => receivedTarget = s;
+ await state.CanTransitionToAsync(target);
+
+ Assert.That(receivedTarget, Is.SameAs(target));
+ }
+
+ ///
+ /// 验证异步状态对多次转换的跟踪能力
+ ///
+ [Test]
+ public async Task AsyncState_Should_Track_MultipleTransitions()
+ {
+ var state = new ConcreteAsyncStateV2();
+ var other = new ConcreteStateV3();
+
+ await state.OnEnterAsync(other);
+ await state.OnExitAsync(other);
+ await state.OnEnterAsync(other);
+ await state.OnExitAsync(null);
+
+ Assert.That(state.EnterCallCount, Is.EqualTo(2));
+ Assert.That(state.ExitCallCount, Is.EqualTo(2));
+ }
}
///
@@ -490,4 +621,114 @@ public sealed class TrackingStateV2 : IState
{
return true;
}
+}
+
+///
+/// 异步具体状态实现类V2版本,用于测试异步状态的基本功能
+///
+public sealed class ConcreteAsyncStateV2 : IState, IAsyncState
+{
+ ///
+ /// 获取或设置是否允许转换
+ ///
+ public bool AllowTransitions { get; set; } = true;
+
+ ///
+ /// 获取进入状态是否被调用的标志
+ ///
+ public bool EnterCalled { get; private set; }
+
+ ///
+ /// 获取退出状态是否被调用的标志
+ ///
+ public bool ExitCalled { get; private set; }
+
+ ///
+ /// 获取进入状态被调用的次数
+ ///
+ public int EnterCallCount { get; private set; }
+
+ ///
+ /// 获取退出状态被调用的次数
+ ///
+ public int ExitCallCount { get; private set; }
+
+ ///
+ /// 获取进入此状态的来源状态
+ ///
+ public IState? EnterFrom { get; private set; }
+
+ ///
+ /// 获取从此状态退出的目标状态
+ ///
+ public IState? ExitTo { get; private set; }
+
+ ///
+ /// 获取或设置转换到目标状态时执行的动作
+ ///
+ public Action? CanTransitionToAsyncAction { get; set; }
+
+ ///
+ /// 异步进入当前状态时调用的方法
+ ///
+ /// 从哪个状态进入
+ public async Task OnEnterAsync(IState? from)
+ {
+ await Task.Delay(1);
+ EnterCalled = true;
+ EnterCallCount++;
+ EnterFrom = from;
+ }
+
+ ///
+ /// 异步退出当前状态时调用的方法
+ ///
+ /// 退出到哪个状态
+ public async Task OnExitAsync(IState? to)
+ {
+ await Task.Delay(1);
+ ExitCalled = true;
+ ExitCallCount++;
+ ExitTo = to;
+ }
+
+ ///
+ /// 异步判断是否可以转换到目标状态
+ ///
+ /// 目标状态
+ /// 如果可以转换则返回true,否则返回false
+ public async Task CanTransitionToAsync(IState target)
+ {
+ await Task.Delay(1);
+ CanTransitionToAsyncAction?.Invoke(target);
+ return AllowTransitions;
+ }
+
+ ///
+ /// 进入当前状态时调用的方法(同步版本,抛出异常表示不应被调用)
+ ///
+ /// 从哪个状态进入
+ public void OnEnter(IState? from)
+ {
+ throw new InvalidOperationException("Sync OnEnter should not be called for async state");
+ }
+
+ ///
+ /// 退出当前状态时调用的方法(同步版本,抛出异常表示不应被调用)
+ ///
+ /// 退出到哪个状态
+ public void OnExit(IState? to)
+ {
+ throw new InvalidOperationException("Sync OnExit should not be called for async state");
+ }
+
+ ///
+ /// 判断是否可以转换到目标状态(同步版本,抛出异常表示不应被调用)
+ ///
+ /// 目标状态
+ /// 如果可以转换则返回true,否则返回false
+ public bool CanTransitionTo(IState target)
+ {
+ throw new InvalidOperationException("Sync CanTransitionTo should not be called for async state");
+ }
}
\ No newline at end of file
diff --git a/GFramework.Core/extensions/ContextAwareExtensions.cs b/GFramework.Core/extensions/ContextAwareExtensions.cs
index 50785e7..52559da 100644
--- a/GFramework.Core/extensions/ContextAwareExtensions.cs
+++ b/GFramework.Core/extensions/ContextAwareExtensions.cs
@@ -6,6 +6,7 @@ using GFramework.Core.Abstractions.query;
using GFramework.Core.Abstractions.rule;
using GFramework.Core.Abstractions.system;
using GFramework.Core.Abstractions.utility;
+using Mediator;
namespace GFramework.Core.extensions;
@@ -19,7 +20,7 @@ public static class ContextAwareExtensions
///
/// 要获取的服务类型
/// 实现 IContextAware 接口的上下文感知对象
- /// 指定类型的服务实例,如果未找到则返回 null
+ /// 指定类型的服务实例,如果未找到则返回 null
/// 当 contextAware 参数为 null 时抛出
public static TService? GetService(this IContextAware contextAware) where TService : class
{
@@ -71,56 +72,6 @@ public static class ContextAwareExtensions
return context.GetUtility();
}
- ///
- /// 发送一个查询请求
- ///
- /// 查询结果类型
- /// 实现 IContextAware 接口的对象
- /// 要发送的查询
- /// 查询结果
- /// 当 contextAware 或 query 为 null 时抛出
- public static TResult SendQuery(this IContextAware contextAware, IQuery query)
- {
- ArgumentNullException.ThrowIfNull(contextAware);
- ArgumentNullException.ThrowIfNull(query);
-
- var context = contextAware.GetContext();
- return context.SendQuery(query);
- }
-
- ///
- /// 发送并异步执行一个无返回值的命令
- ///
- /// 实现 IContextAware 接口的对象
- /// 要发送的命令
- /// 当 contextAware 或 command 为 null 时抛出
- public static async Task SendCommandAsync(this IContextAware contextAware, IAsyncCommand command)
- {
- ArgumentNullException.ThrowIfNull(contextAware);
- ArgumentNullException.ThrowIfNull(command);
-
- var context = contextAware.GetContext();
- await context.SendCommandAsync(command);
- }
-
- ///
- /// 发送并异步执行一个带返回值的命令
- ///
- /// 命令执行结果类型
- /// 实现 IContextAware 接口的对象
- /// 要发送的命令
- /// 命令执行结果
- /// 当 contextAware 或 command 为 null 时抛出
- public static async Task SendCommandAsync(this IContextAware contextAware,
- IAsyncCommand command)
- {
- ArgumentNullException.ThrowIfNull(contextAware);
- ArgumentNullException.ThrowIfNull(command);
-
- var context = contextAware.GetContext();
- return await context.SendCommandAsync(command);
- }
-
///
/// 发送一个事件
@@ -151,39 +102,6 @@ public static class ContextAwareExtensions
context.SendEvent(e);
}
- ///
- /// 发送一个事件
- ///
- /// 命令执行结果类型
- /// 实现 IContextAware 接口的对象
- /// 要发送的命令
- /// 命令执行结果
- /// 当 contextAware 或 command 为 null 时抛出
- public static TResult SendCommand(this IContextAware contextAware, ICommand command)
- {
- ArgumentNullException.ThrowIfNull(contextAware);
- ArgumentNullException.ThrowIfNull(command);
-
- var context = contextAware.GetContext();
- return context.SendCommand(command);
- }
-
- ///
- /// 发送一个无返回结果的命令
- ///
- /// 实现 IContextAware 接口的对象
- /// 要发送的命令
- /// 当 contextAware 或 command 为 null 时抛出
- public static void SendCommand(this IContextAware contextAware, ICommand command)
- {
- ArgumentNullException.ThrowIfNull(contextAware);
- ArgumentNullException.ThrowIfNull(command);
-
- var context = contextAware.GetContext();
- context.SendCommand(command);
- }
-
-
///
/// 注册事件处理器
///
@@ -222,7 +140,7 @@ public static class ContextAwareExtensions
///
/// 要获取的环境对象类型
/// 上下文感知对象
- /// 指定类型的环境对象,如果无法转换则返回null
+ /// 指定类型的环境对象,如果无法转换则返回null
public static T? GetEnvironment(this IContextAware contextAware) where T : class
{
ArgumentNullException.ThrowIfNull(contextAware);
@@ -243,4 +161,338 @@ public static class ContextAwareExtensions
var context = contextAware.GetContext();
return context.GetEnvironment();
}
+
+
+ ///
+ /// [Mediator] 发送命令的同步版本(不推荐,仅用于兼容性)
+ ///
+ /// 命令响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令对象
+ /// 命令执行结果
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static TResponse SendCommand(this IContextAware contextAware,
+ Mediator.ICommand command)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ return context.SendCommand(command);
+ }
+
+ ///
+ /// 发送一个带返回结果的命令
+ ///
+ /// 命令执行结果类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令
+ /// 命令执行结果
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static TResult SendCommand(this IContextAware contextAware,
+ Abstractions.command.ICommand command)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ return context.SendCommand(command);
+ }
+
+ ///
+ /// 发送一个无返回结果的命令
+ ///
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static void SendCommand(this IContextAware contextAware, Abstractions.command.ICommand command)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ context.SendCommand(command);
+ }
+
+
+ ///
+ /// [Mediator] 异步发送命令并返回结果
+ ///
+ /// 命令响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令对象
+ /// 取消令牌,用于取消操作
+ /// 包含命令执行结果的ValueTask
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static ValueTask SendCommandAsync(this IContextAware contextAware,
+ Mediator.ICommand command, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ return context.SendCommandAsync(command, cancellationToken);
+ }
+
+ ///
+ /// 发送并异步执行一个无返回值的命令
+ ///
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static async Task SendCommandAsync(this IContextAware contextAware, IAsyncCommand command)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ await context.SendCommandAsync(command);
+ }
+
+ ///
+ /// 发送并异步执行一个带返回值的命令
+ ///
+ /// 命令执行结果类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令
+ /// 命令执行结果
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static async Task SendCommandAsync(this IContextAware contextAware,
+ IAsyncCommand command)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ return await context.SendCommandAsync(command);
+ }
+
+ ///
+ /// [Mediator] 发送查询的同步版本(不推荐,仅用于兼容性)
+ ///
+ /// 查询响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的查询对象
+ /// 查询结果
+ /// 当 contextAware 或 query 为 null 时抛出
+ public static TResponse SendQuery(this IContextAware contextAware, Mediator.IQuery query)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(query);
+
+ var context = contextAware.GetContext();
+ return context.SendQuery(query);
+ }
+
+ ///
+ /// 发送一个查询请求
+ ///
+ /// 查询结果类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的查询
+ /// 查询结果
+ /// 当 contextAware 或 query 为 null 时抛出
+ public static TResult SendQuery(this IContextAware contextAware, Abstractions.query.IQuery query)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(query);
+
+ var context = contextAware.GetContext();
+ return context.SendQuery(query);
+ }
+
+ ///
+ /// [Mediator] 异步发送查询并返回结果
+ ///
+ /// 查询响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的查询对象
+ /// 取消令牌,用于取消操作
+ /// 包含查询结果的ValueTask
+ /// 当 contextAware 或 query 为 null 时抛出
+ public static ValueTask SendQueryAsync(this IContextAware contextAware,
+ Mediator.IQuery query, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(query);
+
+ var context = contextAware.GetContext();
+ return context.SendQueryAsync(query, cancellationToken);
+ }
+
+ ///
+ /// 异步发送一个查询请求
+ ///
+ /// 查询结果类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的异步查询
+ /// 查询结果
+ /// 当 contextAware 或 query 为 null 时抛出
+ public static async Task SendQueryAsync(this IContextAware contextAware,
+ IAsyncQuery query)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(query);
+
+ var context = contextAware.GetContext();
+ return await context.SendQueryAsync(query);
+ }
+
+ // === 统一请求处理方法 ===
+
+ ///
+ /// 发送请求(统一处理 Command/Query)
+ ///
+ /// 响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的请求
+ /// 取消令牌
+ /// 请求结果
+ /// 当 contextAware 或 request 为 null 时抛出
+ public static ValueTask SendRequestAsync(this IContextAware contextAware,
+ IRequest request, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(request);
+
+ var context = contextAware.GetContext();
+ return context.SendRequestAsync(request, cancellationToken);
+ }
+
+ ///
+ /// 发送请求(同步版本,不推荐)
+ ///
+ /// 响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的请求
+ /// 请求结果
+ /// 当 contextAware 或 request 为 null 时抛出
+ public static TResponse SendRequest(this IContextAware contextAware,
+ IRequest request)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(request);
+
+ var context = contextAware.GetContext();
+ return context.SendRequest(request);
+ }
+
+ ///
+ /// 发布通知(一对多事件)
+ ///
+ /// 通知类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发布的通知
+ /// 取消令牌
+ /// 异步任务
+ /// 当 contextAware 或 notification 为 null 时抛出
+ public static ValueTask PublishAsync(this IContextAware contextAware,
+ TNotification notification, CancellationToken cancellationToken = default)
+ where TNotification : INotification
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(notification);
+
+ var context = contextAware.GetContext();
+ return context.PublishAsync(notification, cancellationToken);
+ }
+
+ ///
+ /// 创建流式请求(用于大数据集)
+ ///
+ /// 响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 流式请求
+ /// 取消令牌
+ /// 异步响应流
+ /// 当 contextAware 或 request 为 null 时抛出
+ public static IAsyncEnumerable CreateStream(this IContextAware contextAware,
+ IStreamRequest request, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(request);
+
+ var context = contextAware.GetContext();
+ return context.CreateStream(request, cancellationToken);
+ }
+
+ // === 便捷扩展方法 ===
+
+ ///
+ /// 发送命令(无返回值)
+ ///
+ /// 命令类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令
+ /// 取消令牌
+ /// 异步任务
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static ValueTask SendAsync(this IContextAware contextAware, TCommand command,
+ CancellationToken cancellationToken = default)
+ where TCommand : IRequest
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ return context.SendAsync(command, cancellationToken);
+ }
+
+ ///
+ /// 发送命令(有返回值)
+ ///
+ /// 响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的命令
+ /// 取消令牌
+ /// 命令执行结果
+ /// 当 contextAware 或 command 为 null 时抛出
+ public static ValueTask SendAsync(this IContextAware contextAware,
+ IRequest command, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(command);
+
+ var context = contextAware.GetContext();
+ return context.SendAsync(command, cancellationToken);
+ }
+
+ ///
+ /// 发送查询
+ ///
+ /// 响应类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发送的查询
+ /// 取消令牌
+ /// 查询结果
+ /// 当 contextAware 或 query 为 null 时抛出
+ public static ValueTask QueryAsync(this IContextAware contextAware,
+ IRequest query, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(query);
+
+ var context = contextAware.GetContext();
+ return context.QueryAsync(query, cancellationToken);
+ }
+
+ ///
+ /// 发布事件通知
+ ///
+ /// 通知类型
+ /// 实现 IContextAware 接口的对象
+ /// 要发布的通知
+ /// 取消令牌
+ /// 异步任务
+ /// 当 contextAware 或 notification 为 null 时抛出
+ public static ValueTask PublishEventAsync(this IContextAware contextAware,
+ TNotification notification, CancellationToken cancellationToken = default)
+ where TNotification : INotification
+ {
+ ArgumentNullException.ThrowIfNull(contextAware);
+ ArgumentNullException.ThrowIfNull(notification);
+
+ var context = contextAware.GetContext();
+ return context.PublishEventAsync(notification, cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/GFramework.Core/state/StateMachineSystem.cs b/GFramework.Core/state/StateMachineSystem.cs
index 3a3692f..6bed3bb 100644
--- a/GFramework.Core/state/StateMachineSystem.cs
+++ b/GFramework.Core/state/StateMachineSystem.cs
@@ -1,4 +1,4 @@
-using GFramework.Core.Abstractions.architecture;
+using GFramework.Core.Abstractions.architecture;
using GFramework.Core.Abstractions.enums;
using GFramework.Core.Abstractions.rule;
using GFramework.Core.Abstractions.state;
@@ -87,4 +87,21 @@ public class StateMachineSystem : StateMachine, IStateMachineSystem
NewState = Current
});
}
+
+ ///
+ /// 异步内部状态切换方法,重写基类方法以添加状态变更事件通知功能
+ ///
+ /// 要切换到的下一个状态
+ protected override async Task ChangeInternalAsync(IState next)
+ {
+ var old = Current;
+ await base.ChangeInternalAsync(next);
+
+ // 发送状态变更事件,通知监听者状态已发生改变
+ this.SendEvent(new StateChangedEvent
+ {
+ OldState = old,
+ NewState = Current
+ });
+ }
}
\ No newline at end of file