diff --git a/GFramework.Core.Abstractions/Concurrency/LockInfo.cs b/GFramework.Core.Abstractions/Concurrency/LockInfo.cs new file mode 100644 index 0000000..2f8cc1f --- /dev/null +++ b/GFramework.Core.Abstractions/Concurrency/LockInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2025 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Runtime.InteropServices; + +namespace GFramework.Core.Abstractions.Concurrency; + +/// +/// 锁信息(用于调试) +/// +[StructLayout(LayoutKind.Auto)] +public readonly struct LockInfo +{ + /// + /// 锁的键。 + /// + public string Key { get; init; } + + /// + /// 当前引用计数。 + /// + public int ReferenceCount { get; init; } + + /// + /// 最后访问时间戳(Environment.TickCount64)。 + /// + public long LastAccessTicks { get; init; } + + /// + /// 等待队列长度(近似值)。 + /// 注意:这是一个基于 SemaphoreSlim.CurrentCount 的近似指示器, + /// 当 CurrentCount == 0 时表示锁被持有且可能有等待者,返回 1; + /// 否则返回 0。这不是精确的等待者数量,仅用于调试参考。 + /// + public int WaitingCount { get; init; } +} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/Concurrency/LockStatistics.cs b/GFramework.Core.Abstractions/Concurrency/LockStatistics.cs index c875496..f86bb68 100644 --- a/GFramework.Core.Abstractions/Concurrency/LockStatistics.cs +++ b/GFramework.Core.Abstractions/Concurrency/LockStatistics.cs @@ -11,11 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Runtime.InteropServices; + namespace GFramework.Core.Abstractions.Concurrency; /// /// 锁统计信息 /// +[StructLayout(LayoutKind.Auto)] public readonly struct LockStatistics { /// @@ -37,33 +40,4 @@ public readonly struct LockStatistics /// 累计清理的锁数量 /// public int TotalCleaned { get; init; } -} - -/// -/// 锁信息(用于调试) -/// -public readonly struct LockInfo -{ - /// - /// 锁的键 - /// - public string Key { get; init; } - - /// - /// 当前引用计数 - /// - public int ReferenceCount { get; init; } - - /// - /// 最后访问时间戳(Environment.TickCount64) - /// - public long LastAccessTicks { get; init; } - - /// - /// 等待队列长度(近似值) - /// 注意:这是一个基于 SemaphoreSlim.CurrentCount 的近似指示器, - /// 当 CurrentCount == 0 时表示锁被持有且可能有等待者,返回 1; - /// 否则返回 0。这不是精确的等待者数量,仅用于调试参考。 - /// - public int WaitingCount { get; init; } } \ No newline at end of file diff --git a/GFramework.Core.Abstractions/Pause/IPauseStackManager.cs b/GFramework.Core.Abstractions/Pause/IPauseStackManager.cs index 7c40ab7..11e24b9 100644 --- a/GFramework.Core.Abstractions/Pause/IPauseStackManager.cs +++ b/GFramework.Core.Abstractions/Pause/IPauseStackManager.cs @@ -75,7 +75,9 @@ public interface IPauseStackManager : IContextUtility void UnregisterHandler(IPauseHandler handler); /// - /// 暂停状态变化事件 + /// 暂停状态变化事件。 + /// 事件遵循标准 .NET 事件模式,事件源为触发通知的暂停管理器实例, + /// 事件数据由 提供。 /// - event Action? OnPauseStateChanged; + event EventHandler? OnPauseStateChanged; } \ No newline at end of file diff --git a/GFramework.Core.Abstractions/Pause/PauseStateChangedEventArgs.cs b/GFramework.Core.Abstractions/Pause/PauseStateChangedEventArgs.cs new file mode 100644 index 0000000..72a84dd --- /dev/null +++ b/GFramework.Core.Abstractions/Pause/PauseStateChangedEventArgs.cs @@ -0,0 +1,30 @@ +namespace GFramework.Core.Abstractions.Pause; + +/// +/// 表示暂停状态变化事件的数据。 +/// 该类型用于向事件订阅者传递暂停组以及该组变化后的暂停状态。 +/// +public sealed class PauseStateChangedEventArgs : EventArgs +{ + /// + /// 初始化 的新实例。 + /// + /// 发生状态变化的暂停组。 + /// 暂停组变化后的新状态。 + public PauseStateChangedEventArgs(PauseGroup group, bool isPaused) + { + Group = group; + IsPaused = isPaused; + } + + /// + /// 获取发生状态变化的暂停组。 + /// + public PauseGroup Group { get; } + + /// + /// 获取暂停组变化后的新状态。 + /// 为 表示进入暂停,为 表示恢复运行。 + /// + public bool IsPaused { get; } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/Pause/PauseStackManagerTests.cs b/GFramework.Core.Tests/Pause/PauseStackManagerTests.cs index f682c1b..4705357 100644 --- a/GFramework.Core.Tests/Pause/PauseStackManagerTests.cs +++ b/GFramework.Core.Tests/Pause/PauseStackManagerTests.cs @@ -1,6 +1,5 @@ using GFramework.Core.Abstractions.Pause; using GFramework.Core.Pause; -using NUnit.Framework; namespace GFramework.Core.Tests.Pause; @@ -220,11 +219,11 @@ public class PauseStackManagerTests PauseGroup? eventGroup = null; bool? eventIsPaused = null; - _manager.OnPauseStateChanged += (group, isPaused) => + _manager.OnPauseStateChanged += (_, e) => { eventTriggered = true; - eventGroup = group; - eventIsPaused = isPaused; + eventGroup = e.Group; + eventIsPaused = e.IsPaused; }; _manager.Push("Test", PauseGroup.Gameplay); @@ -243,10 +242,10 @@ public class PauseStackManagerTests var token = _manager.Push("Test"); bool eventTriggered = false; - _manager.OnPauseStateChanged += (group, isPaused) => + _manager.OnPauseStateChanged += (_, e) => { eventTriggered = true; - Assert.That(isPaused, Is.False); + Assert.That(e.IsPaused, Is.False); }; _manager.Pop(token); diff --git a/GFramework.Core/Coroutine/CoroutineSlot.cs b/GFramework.Core/Coroutine/CoroutineSlot.cs index 38ade65..7821e48 100644 --- a/GFramework.Core/Coroutine/CoroutineSlot.cs +++ b/GFramework.Core/Coroutine/CoroutineSlot.cs @@ -17,11 +17,6 @@ internal sealed class CoroutineSlot /// public CoroutineHandle Handle; - /// - /// 协程是否已经开始执行 - /// - public bool HasStarted; - /// /// 协程的优先级 /// diff --git a/GFramework.Core/Events/PriorityEvent.cs b/GFramework.Core/Events/PriorityEvent.cs index 8704267..8555e93 100644 --- a/GFramework.Core/Events/PriorityEvent.cs +++ b/GFramework.Core/Events/PriorityEvent.cs @@ -208,13 +208,10 @@ public class PriorityEvent : IEvent MergeAndSortHandlers(T t) { var (normalSnapshot, contextSnapshot) = CreateSnapshots(); - // 使用快照避免迭代期间修改 + // 使用统一的投影方法显式固定元组的可空标注,避免 LINQ 在 Concat 时推断出不兼容的签名。 return normalSnapshot - .Select(h => (h.Priority, Handler: (Action?)(() => h.Handler.Invoke(t)), - ContextHandler: (Action>?)null, IsContext: false)) - .Concat(contextSnapshot - .Select(h => (h.Priority, Handler: (Action?)null, - ContextHandler: (Action>?)h.Handler, IsContext: true))) + .Select(h => CreateNormalHandlerInvocation(h, t)) + .Concat(contextSnapshot.Select(CreateContextHandlerInvocation)) .OrderByDescending(h => h.Priority) .ToList(); } @@ -289,6 +286,29 @@ public class PriorityEvent : IEvent } } + /// + /// 将普通事件处理器转换为统一的调用描述。 + /// + /// 要包装的普通处理器。 + /// 当前触发的事件数据。 + /// 可与上下文处理器合并排序的统一调用描述。 + private static (int Priority, Action? Handler, Action>? ContextHandler, bool IsContext) + CreateNormalHandlerInvocation(EventHandler handler, T t) + { + return (handler.Priority, () => handler.Handler.Invoke(t), null, false); + } + + /// + /// 将上下文事件处理器转换为统一的调用描述。 + /// + /// 要包装的上下文处理器。 + /// 可与普通处理器合并排序的统一调用描述。 + private static (int Priority, Action? Handler, Action>? ContextHandler, bool IsContext) + CreateContextHandlerInvocation(ContextEventHandler handler) + { + return (handler.Priority, null, handler.Handler, true); + } + /// /// 事件处理器包装类,包含处理器和优先级 /// diff --git a/GFramework.Core/Pause/PauseStackManager.cs b/GFramework.Core/Pause/PauseStackManager.cs index e11e171..dd15fe3 100644 --- a/GFramework.Core/Pause/PauseStackManager.cs +++ b/GFramework.Core/Pause/PauseStackManager.cs @@ -80,7 +80,7 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs // 触发事件 try { - OnPauseStateChanged?.Invoke(group, false); + RaisePauseStateChanged(group, false); } catch (Exception ex) { @@ -97,7 +97,7 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs /// /// 暂停状态变化事件,当暂停状态发生改变时触发。 /// - public event Action? OnPauseStateChanged; + public event EventHandler? OnPauseStateChanged; /// /// 推入一个新的暂停请求到指定的暂停组中。 @@ -488,7 +488,18 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs } // 触发事件 - OnPauseStateChanged?.Invoke(group, isPaused); + RaisePauseStateChanged(group, isPaused); + } + + /// + /// 以标准事件模式发布暂停状态变化事件。 + /// 所有状态变更路径都通过该方法创建统一的事件参数,避免不同调用点出现不一致的载荷。 + /// + /// 发生状态变化的暂停组。 + /// 暂停组变化后的新状态。 + private void RaisePauseStateChanged(PauseGroup group, bool isPaused) + { + OnPauseStateChanged?.Invoke(this, new PauseStateChangedEventArgs(group, isPaused)); } /// diff --git a/GFramework.Game.Abstractions/Scene/ISceneBehavior.cs b/GFramework.Game.Abstractions/Scene/ISceneBehavior.cs index 2bb07ee..f56f5d8 100644 --- a/GFramework.Game.Abstractions/Scene/ISceneBehavior.cs +++ b/GFramework.Game.Abstractions/Scene/ISceneBehavior.cs @@ -23,9 +23,9 @@ public interface ISceneBehavior : IRoute { /// /// 获取场景的唯一标识符。 - /// 用于区分不同的场景实例。 + /// 该成员显式细化了 在场景路由中的语义,用于区分不同的场景实例。 /// - string Key { get; } + new string Key { get; } /// /// 获取场景的原始对象。 diff --git a/GFramework.Game.Abstractions/Scene/ISceneRouteGuard.cs b/GFramework.Game.Abstractions/Scene/ISceneRouteGuard.cs index eb3e70e..8680c14 100644 --- a/GFramework.Game.Abstractions/Scene/ISceneRouteGuard.cs +++ b/GFramework.Game.Abstractions/Scene/ISceneRouteGuard.cs @@ -31,8 +31,9 @@ public interface ISceneRouteGuard : IRouteGuard /// /// 异步检查是否允许离开指定场景。 + /// 该成员显式细化了通用路由守卫的离开检查,使场景守卫在 API 文档中保持场景语义。 /// /// 当前场景的唯一标识符。 /// 如果允许离开则返回 true,否则返回 false。 - ValueTask CanLeaveAsync(string sceneKey); + new ValueTask CanLeaveAsync(string sceneKey); } \ No newline at end of file diff --git a/GFramework.Game.Abstractions/UI/IUiPageBehavior.cs b/GFramework.Game.Abstractions/UI/IUiPageBehavior.cs index 8e84067..51e693a 100644 --- a/GFramework.Game.Abstractions/UI/IUiPageBehavior.cs +++ b/GFramework.Game.Abstractions/UI/IUiPageBehavior.cs @@ -48,10 +48,10 @@ public interface IUiPageBehavior : IRoute /// - /// 获取键值 + /// 获取页面的唯一键值,并显式细化 在 UI 路由中的语义。 /// /// 返回当前对象的键标识符 - string Key { get; } + new string Key { get; } /// diff --git a/GFramework.Game.Abstractions/UI/IUiRouteGuard.cs b/GFramework.Game.Abstractions/UI/IUiRouteGuard.cs index fff03c9..d1a7ee4 100644 --- a/GFramework.Game.Abstractions/UI/IUiRouteGuard.cs +++ b/GFramework.Game.Abstractions/UI/IUiRouteGuard.cs @@ -17,9 +17,10 @@ public interface IUiRouteGuard : IRouteGuard ValueTask CanEnterAsync(string uiKey, IUiPageEnterParam? param); /// - /// 离开UI前的检查 + /// 离开UI前的检查。 + /// 该成员显式细化了通用路由守卫的离开检查,使 UI 守卫在 API 文档中保持 UI 语义。 /// /// 当前UI标识符 /// true表示允许离开,false表示拦截 - ValueTask CanLeaveAsync(string uiKey); + new ValueTask CanLeaveAsync(string uiKey); } \ No newline at end of file diff --git a/docs/zh-CN/core/pause.md b/docs/zh-CN/core/pause.md index 7f81f95..b4b9ca2 100644 --- a/docs/zh-CN/core/pause.md +++ b/docs/zh-CN/core/pause.md @@ -100,8 +100,8 @@ void ClearAll(); void RegisterHandler(IPauseHandler handler); void UnregisterHandler(IPauseHandler handler); -// 状态变化事件 -event Action? OnPauseStateChanged; +// 状态变化事件 +event EventHandler? OnPauseStateChanged; ``` ## 基本用法 @@ -377,16 +377,16 @@ public partial class PauseIndicator : IController _pauseManager.OnPauseStateChanged += OnPauseStateChanged; } - private void OnPauseStateChanged(PauseGroup group, bool isPaused) - { - Console.WriteLine($"暂停状态变化: 组={group}, 暂停={isPaused}"); - - if (group == PauseGroup.Global) - { - if (isPaused) - { - ShowPauseIndicator(); - } + private void OnPauseStateChanged(object? sender, PauseStateChangedEventArgs e) + { + Console.WriteLine($"暂停状态变化: 组={e.Group}, 暂停={e.IsPaused}"); + + if (e.Group == PauseGroup.Global) + { + if (e.IsPaused) + { + ShowPauseIndicator(); + } else { HidePauseIndicator(); @@ -705,7 +705,7 @@ public partial class ProperCleanup : IController _pauseManager.OnPauseStateChanged -= OnPauseChanged; } - private void OnPauseChanged(PauseGroup group, bool isPaused) { } + private void OnPauseChanged(object? sender, PauseStateChangedEventArgs e) { } } ``` @@ -743,13 +743,13 @@ public partial class PauseMenu : Control // 方案 2: 监听暂停事件 var pauseManager = this.GetUtility(); - pauseManager.OnPauseStateChanged += (group, isPaused) => - { - if (group == PauseGroup.Global) - { - Visible = isPaused; - } - }; + pauseManager.OnPauseStateChanged += (_, e) => + { + if (e.Group == PauseGroup.Global) + { + Visible = e.IsPaused; + } + }; } } ``` @@ -887,15 +887,15 @@ public class PauseEventBridge : AbstractSystem { var pauseManager = this.GetUtility(); - pauseManager.OnPauseStateChanged += (group, isPaused) => - { - // 发送暂停事件 - this.SendEvent(new GamePausedEvent - { - Group = group, - IsPaused = isPaused - }); - }; + pauseManager.OnPauseStateChanged += (_, e) => + { + // 发送暂停事件 + this.SendEvent(new GamePausedEvent + { + Group = e.Group, + IsPaused = e.IsPaused + }); + }; } } diff --git a/docs/zh-CN/godot/pause.md b/docs/zh-CN/godot/pause.md index a511316..7f40ae4 100644 --- a/docs/zh-CN/godot/pause.md +++ b/docs/zh-CN/godot/pause.md @@ -44,7 +44,7 @@ public interface IPauseStackManager : IContextUtility int GetPauseDepth(PauseGroup group = PauseGroup.Global); // 暂停状态变化事件 - event Action? OnPauseStateChanged; + event EventHandler? OnPauseStateChanged; } ``` @@ -477,12 +477,12 @@ public partial class PauseIndicator : Label pauseManager.OnPauseStateChanged -= OnPauseStateChanged; } - private void OnPauseStateChanged(PauseGroup group, bool isPaused) + private void OnPauseStateChanged(object? sender, PauseStateChangedEventArgs e) { - if (group == PauseGroup.Global) + if (e.Group == PauseGroup.Global) { - Text = isPaused ? "游戏已暂停" : "游戏运行中"; - Visible = isPaused; + Text = e.IsPaused ? "游戏已暂停" : "游戏运行中"; + Visible = e.IsPaused; } } } @@ -502,16 +502,16 @@ public partial class PauseDebugger : Node pauseManager.OnPauseStateChanged += OnPauseStateChanged; } - private void OnPauseStateChanged(PauseGroup group, bool isPaused) + private void OnPauseStateChanged(object? sender, PauseStateChangedEventArgs e) { var pauseManager = this.GetUtility(); GD.Print($"=== 暂停状态变化 ==="); - GD.Print($"组: {group}"); - GD.Print($"状态: {(isPaused ? "暂停" : "恢复")}"); - GD.Print($"深度: {pauseManager.GetPauseDepth(group)}"); + GD.Print($"组: {e.Group}"); + GD.Print($"状态: {(e.IsPaused ? "暂停" : "恢复")}"); + GD.Print($"深度: {pauseManager.GetPauseDepth(e.Group)}"); - var reasons = pauseManager.GetPauseReasons(group); + var reasons = pauseManager.GetPauseReasons(e.Group); if (reasons.Count > 0) { GD.Print($"原因:"); @@ -609,9 +609,9 @@ public partial class PauseDebugger : Node 7. **使用事件监听暂停状态**:实现响应式 UI ```csharp - pauseManager.OnPauseStateChanged += (group, isPaused) => + pauseManager.OnPauseStateChanged += (_, e) => { - UpdateUI(isPaused); + UpdateUI(e.IsPaused); }; ```