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