From e738e59a58b32ffd603904a80beb5c658fe4ad4a Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 6 Mar 2026 16:09:40 +0800
Subject: [PATCH 1/3] =?UTF-8?q?feat(events):=20=E6=B7=BB=E5=8A=A0=E5=A2=9E?=
=?UTF-8?q?=E5=BC=BA=E4=BA=8B=E4=BB=B6=E6=80=BB=E7=BA=BF=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E8=BF=87=E6=BB=A4=E5=99=A8=E5=92=8C=E7=BB=9F=E8=AE=A1=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现 EnhancedEventBus 支持过滤器、弱引用事件和统计功能
- 添加 FilterableEvent 和 WeakEvent 支持事件过滤和弱引用订阅
- 实现 PredicateEventFilter 和 SamplingEventFilter 事件过滤器
- 添加 EventStatistics 统计事件发布、处理和失败次数
- 实现完整的单元测试验证过滤器和统计功能
---
.../events/IEventFilter.cs | 16 ++
.../events/IEventStatistics.cs | 58 +++++
.../events/EventFilterTests.cs | 188 ++++++++++++++
.../events/EventStatisticsTests.cs | 176 +++++++++++++
.../events/WeakEventTests.cs | 206 +++++++++++++++
GFramework.Core/events/EnhancedEventBus.cs | 242 ++++++++++++++++++
GFramework.Core/events/EventStatistics.cs | 159 ++++++++++++
GFramework.Core/events/FilterableEvent.cs | 155 +++++++++++
GFramework.Core/events/WeakEvent.cs | 154 +++++++++++
.../events/filters/PredicateEventFilter.cs | 28 ++
.../events/filters/SamplingEventFilter.cs | 40 +++
11 files changed, 1422 insertions(+)
create mode 100644 GFramework.Core.Abstractions/events/IEventFilter.cs
create mode 100644 GFramework.Core.Abstractions/events/IEventStatistics.cs
create mode 100644 GFramework.Core.Tests/events/EventFilterTests.cs
create mode 100644 GFramework.Core.Tests/events/EventStatisticsTests.cs
create mode 100644 GFramework.Core.Tests/events/WeakEventTests.cs
create mode 100644 GFramework.Core/events/EnhancedEventBus.cs
create mode 100644 GFramework.Core/events/EventStatistics.cs
create mode 100644 GFramework.Core/events/FilterableEvent.cs
create mode 100644 GFramework.Core/events/WeakEvent.cs
create mode 100644 GFramework.Core/events/filters/PredicateEventFilter.cs
create mode 100644 GFramework.Core/events/filters/SamplingEventFilter.cs
diff --git a/GFramework.Core.Abstractions/events/IEventFilter.cs b/GFramework.Core.Abstractions/events/IEventFilter.cs
new file mode 100644
index 0000000..d97b545
--- /dev/null
+++ b/GFramework.Core.Abstractions/events/IEventFilter.cs
@@ -0,0 +1,16 @@
+namespace GFramework.Core.Abstractions.events;
+
+///
+/// 事件过滤器接口
+/// 用于在事件触发前进行条件判断,决定是否允许事件传递给监听器
+///
+/// 事件类型
+public interface IEventFilter
+{
+ ///
+ /// 判断事件是否应该被过滤(阻止传递)
+ ///
+ /// 事件数据
+ /// 如果返回 true,则事件被过滤(不传递给监听器);如果返回 false,则允许传递
+ bool ShouldFilter(T eventData);
+}
\ No newline at end of file
diff --git a/GFramework.Core.Abstractions/events/IEventStatistics.cs b/GFramework.Core.Abstractions/events/IEventStatistics.cs
new file mode 100644
index 0000000..23adadb
--- /dev/null
+++ b/GFramework.Core.Abstractions/events/IEventStatistics.cs
@@ -0,0 +1,58 @@
+namespace GFramework.Core.Abstractions.events;
+
+///
+/// 事件统计信息接口
+/// 提供事件系统的性能统计数据
+///
+public interface IEventStatistics
+{
+ ///
+ /// 获取总事件发布数量
+ ///
+ long TotalPublished { get; }
+
+ ///
+ /// 获取总事件处理数量(监听器调用次数)
+ ///
+ long TotalHandled { get; }
+
+ ///
+ /// 获取总事件处理失败数量
+ ///
+ long TotalFailed { get; }
+
+ ///
+ /// 获取当前活跃的事件类型数量
+ ///
+ int ActiveEventTypes { get; }
+
+ ///
+ /// 获取当前活跃的监听器总数
+ ///
+ int ActiveListeners { get; }
+
+ ///
+ /// 获取指定事件类型的发布次数
+ ///
+ /// 事件类型名称
+ /// 发布次数
+ long GetPublishCount(string eventType);
+
+ ///
+ /// 获取指定事件类型的监听器数量
+ ///
+ /// 事件类型名称
+ /// 监听器数量
+ int GetListenerCount(string eventType);
+
+ ///
+ /// 重置统计数据
+ ///
+ void Reset();
+
+ ///
+ /// 生成统计报告
+ ///
+ /// 格式化的统计报告字符串
+ string GenerateReport();
+}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/events/EventFilterTests.cs b/GFramework.Core.Tests/events/EventFilterTests.cs
new file mode 100644
index 0000000..1417dc0
--- /dev/null
+++ b/GFramework.Core.Tests/events/EventFilterTests.cs
@@ -0,0 +1,188 @@
+using GFramework.Core.events;
+using GFramework.Core.events.filters;
+
+namespace GFramework.Core.Tests.events;
+
+///
+/// 事件过滤器功能测试
+///
+public sealed class EventFilterTests
+{
+ [Test]
+ public void PredicateFilter_ShouldFilterBasedOnCondition()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(new PredicateEventFilter(e => e.Value < 10)); // 过滤 Value < 10 的事件
+
+ // Act
+ eventBus.SendFilterable(new TestEvent { Value = 5, Message = "Filtered" });
+ eventBus.SendFilterable(new TestEvent { Value = 15, Message = "Passed" });
+ eventBus.SendFilterable(new TestEvent { Value = 20, Message = "Passed" });
+
+ // Assert
+ Assert.That(receivedEvents, Has.Count.EqualTo(2));
+ Assert.That(receivedEvents[0].Value, Is.EqualTo(15));
+ Assert.That(receivedEvents[1].Value, Is.EqualTo(20));
+ }
+
+ [Test]
+ public void SamplingFilter_ShouldFilterBasedOnSamplingRate()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(new SamplingEventFilter(0.5)); // 50% 采样率
+
+ // Act
+ for (var i = 0; i < 100; i++)
+ {
+ eventBus.SendFilterable(new TestEvent { Value = i });
+ }
+
+ // Assert - 应该接收到大约 50 个事件
+ Assert.That(receivedEvents.Count, Is.InRange(45, 55));
+ }
+
+ [Test]
+ public void SamplingFilter_WithZeroRate_ShouldFilterAllEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(new SamplingEventFilter(0.0)); // 0% 采样率
+
+ // Act
+ for (var i = 0; i < 10; i++)
+ {
+ eventBus.SendFilterable(new TestEvent { Value = i });
+ }
+
+ // Assert
+ Assert.That(receivedEvents, Is.Empty);
+ }
+
+ [Test]
+ public void SamplingFilter_WithFullRate_ShouldPassAllEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(new SamplingEventFilter(1.0)); // 100% 采样率
+
+ // Act
+ for (var i = 0; i < 10; i++)
+ {
+ eventBus.SendFilterable(new TestEvent { Value = i });
+ }
+
+ // Assert
+ Assert.That(receivedEvents, Has.Count.EqualTo(10));
+ }
+
+ [Test]
+ public void MultipleFilters_ShouldApplyAllFilters()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(new PredicateEventFilter(e => e.Value < 10)); // 过滤 < 10
+ eventBus.AddFilter(new PredicateEventFilter(e => e.Value > 50)); // 过滤 > 50
+
+ // Act
+ for (var i = 0; i < 100; i++)
+ {
+ eventBus.SendFilterable(new TestEvent { Value = i });
+ }
+
+ // Assert - 只有 10-50 之间的事件通过
+ Assert.That(receivedEvents, Has.Count.EqualTo(41)); // 10 到 50 包含边界
+ Assert.That(receivedEvents.All(e => e.Value >= 10 && e.Value <= 50), Is.True);
+ }
+
+ [Test]
+ public void RemoveFilter_ShouldStopFiltering()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+ var filter = new PredicateEventFilter(e => e.Value < 10);
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(filter);
+
+ // Act - 发送事件(应该被过滤)
+ eventBus.SendFilterable(new TestEvent { Value = 5 });
+ Assert.That(receivedEvents, Is.Empty);
+
+ // 移除过滤器
+ eventBus.RemoveFilter(filter);
+
+ // 再次发送事件(应该通过)
+ eventBus.SendFilterable(new TestEvent { Value = 5 });
+
+ // Assert
+ Assert.That(receivedEvents, Has.Count.EqualTo(1));
+ }
+
+ [Test]
+ public void ClearFilters_ShouldRemoveAllFilters()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedEvents = new List();
+
+ eventBus.RegisterFilterable(e => receivedEvents.Add(e));
+ eventBus.AddFilter(new PredicateEventFilter(e => e.Value < 10));
+ eventBus.AddFilter(new PredicateEventFilter(e => e.Value > 50));
+
+ // Act - 发送事件(应该被过滤)
+ eventBus.SendFilterable(new TestEvent { Value = 5 });
+ Assert.That(receivedEvents, Is.Empty);
+
+ // 清除所有过滤器
+ eventBus.ClearFilters();
+
+ // 再次发送事件(应该通过)
+ eventBus.SendFilterable(new TestEvent { Value = 5 });
+
+ // Assert
+ Assert.That(receivedEvents, Has.Count.EqualTo(1));
+ }
+
+ [Test]
+ public void SamplingFilter_InvalidRate_ShouldThrowException()
+ {
+ // Act & Assert
+ Assert.Throws(() =>
+ new SamplingEventFilter(-0.1));
+
+ Assert.Throws(() =>
+ new SamplingEventFilter(1.1));
+ }
+
+ [Test]
+ public void PredicateFilter_NullPredicate_ShouldThrowException()
+ {
+ // Act & Assert
+ Assert.Throws(() =>
+ new PredicateEventFilter(null!));
+ }
+
+ private sealed class TestEvent
+ {
+ public int Value { get; init; }
+ public string Message { get; init; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/events/EventStatisticsTests.cs b/GFramework.Core.Tests/events/EventStatisticsTests.cs
new file mode 100644
index 0000000..a683d00
--- /dev/null
+++ b/GFramework.Core.Tests/events/EventStatisticsTests.cs
@@ -0,0 +1,176 @@
+using GFramework.Core.events;
+
+namespace GFramework.Core.Tests.events;
+
+///
+/// 事件统计功能测试
+///
+public sealed class EventStatisticsTests
+{
+ [Test]
+ public void Statistics_WhenDisabled_ShouldBeNull()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: false);
+
+ // Act & Assert
+ Assert.That(eventBus.Statistics, Is.Null);
+ }
+
+ [Test]
+ public void Statistics_WhenEnabled_ShouldNotBeNull()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ // Act & Assert
+ Assert.That(eventBus.Statistics, Is.Not.Null);
+ }
+
+ [Test]
+ public void TotalPublished_ShouldTrackPublishedEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ // Act
+ eventBus.SendFilterable(new TestEvent { Message = "Test1" });
+ eventBus.SendFilterable(new TestEvent { Message = "Test2" });
+ eventBus.SendFilterable(new TestEvent { Message = "Test3" });
+
+ // Assert
+ Assert.That(eventBus.Statistics!.TotalPublished, Is.EqualTo(3));
+ }
+
+ [Test]
+ public void TotalHandled_ShouldTrackHandledEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+ var handledCount = 0;
+
+ eventBus.RegisterFilterable(_ => handledCount++);
+ eventBus.RegisterFilterable(_ => handledCount++);
+
+ // Act
+ eventBus.SendFilterable(new TestEvent { Message = "Test" });
+
+ // Assert
+ Assert.That(eventBus.Statistics!.TotalHandled, Is.EqualTo(2));
+ Assert.That(handledCount, Is.EqualTo(2));
+ }
+
+ [Test]
+ public void TotalFailed_ShouldTrackFailedEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ eventBus.RegisterFilterable(_ => throw new InvalidOperationException("Test exception"));
+
+ // Act & Assert
+ Assert.Throws(() =>
+ eventBus.SendFilterable(new TestEvent { Message = "Test" }));
+
+ Assert.That(eventBus.Statistics!.TotalFailed, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void GetPublishCount_ShouldReturnCorrectCount()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ // Act
+ eventBus.SendFilterable(new TestEvent { Message = "Test1" });
+ eventBus.SendFilterable(new TestEvent { Message = "Test2" });
+
+ // Assert
+ Assert.That(eventBus.Statistics!.GetPublishCount(nameof(TestEvent)), Is.EqualTo(2));
+ }
+
+ [Test]
+ public void GetListenerCount_ShouldReturnCorrectCount()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ // Act
+ eventBus.RegisterFilterable(_ => { });
+ eventBus.RegisterFilterable(_ => { });
+
+ // Assert
+ Assert.That(eventBus.Statistics!.GetListenerCount(nameof(TestEvent)), Is.EqualTo(2));
+ }
+
+ [Test]
+ public void Reset_ShouldClearAllStatistics()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ eventBus.RegisterFilterable(_ => { });
+ eventBus.SendFilterable(new TestEvent { Message = "Test" });
+
+ // Act
+ eventBus.Statistics!.Reset();
+
+ // Assert
+ Assert.That(eventBus.Statistics.TotalPublished, Is.EqualTo(0));
+ Assert.That(eventBus.Statistics.TotalHandled, Is.EqualTo(0));
+ Assert.That(eventBus.Statistics.TotalFailed, Is.EqualTo(0));
+ Assert.That(eventBus.Statistics.GetPublishCount(nameof(TestEvent)), Is.EqualTo(0));
+ }
+
+ [Test]
+ public void GenerateReport_ShouldReturnFormattedString()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ eventBus.RegisterFilterable(_ => { });
+ eventBus.SendFilterable(new TestEvent { Message = "Test" });
+
+ // Act
+ var report = eventBus.Statistics!.GenerateReport();
+
+ // Assert
+ Assert.That(report, Is.Not.Null);
+ Assert.That(report, Does.Contain("事件统计报告"));
+ Assert.That(report, Does.Contain("总发布数"));
+ Assert.That(report, Does.Contain("总处理数"));
+ }
+
+ [Test]
+ public void Statistics_ShouldBeThreadSafe()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ eventBus.RegisterFilterable(_ => { });
+
+ // Act - 并发发送事件
+ var tasks = new List();
+ for (var i = 0; i < 10; i++)
+ {
+ tasks.Add(Task.Run(() =>
+ {
+ for (var j = 0; j < 100; j++)
+ {
+ eventBus.SendFilterable(new TestEvent { Message = $"Test-{j}" });
+ }
+ }));
+ }
+
+ Task.WaitAll(tasks.ToArray());
+
+ // Assert
+ Assert.That(eventBus.Statistics!.TotalPublished, Is.EqualTo(1000));
+ Assert.That(eventBus.Statistics.TotalHandled, Is.EqualTo(1000));
+ }
+
+ private sealed class TestEvent
+ {
+ public string Message { get; init; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/events/WeakEventTests.cs b/GFramework.Core.Tests/events/WeakEventTests.cs
new file mode 100644
index 0000000..02a0bd0
--- /dev/null
+++ b/GFramework.Core.Tests/events/WeakEventTests.cs
@@ -0,0 +1,206 @@
+using GFramework.Core.events;
+
+namespace GFramework.Core.Tests.events;
+
+///
+/// 弱引用事件功能测试
+///
+public sealed class WeakEventTests
+{
+ [Test]
+ public void WeakEvent_ShouldReceiveEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var listener = new EventListener();
+
+ eventBus.RegisterWeak(listener.OnEvent);
+
+ // Act
+ eventBus.SendWeak(new TestEvent { Message = "Test1" });
+ eventBus.SendWeak(new TestEvent { Message = "Test2" });
+
+ // Assert
+ Assert.That(listener.ReceivedCount, Is.EqualTo(2));
+ }
+
+ [Test]
+ public void WeakEvent_WhenListenerCollected_ShouldNotReceiveEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var receivedCount = 0;
+
+ void RegisterAndCollect()
+ {
+ var listener = new EventListener();
+ eventBus.RegisterWeak(listener.OnEvent);
+
+ // 发送事件,监听器应该接收到
+ eventBus.SendWeak(new TestEvent { Message = "Test1" });
+ receivedCount = listener.ReceivedCount;
+ }
+
+ RegisterAndCollect();
+
+ // 强制垃圾回收
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
+ // Act - 再次发送事件
+ eventBus.SendWeak(new TestEvent { Message = "Test2" });
+
+ // Assert - 第一次应该接收到,第二次不应该接收到(因为监听器已被回收)
+ Assert.That(receivedCount, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void WeakEvent_Cleanup_ShouldRemoveCollectedReferences()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ void RegisterAndCollect()
+ {
+ var listener = new EventListener();
+ eventBus.RegisterWeak(listener.OnEvent);
+ }
+
+ RegisterAndCollect();
+
+ // 强制垃圾回收
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
+ // Act
+ eventBus.CleanupWeak();
+
+ // Assert - 监听器数量应该为 0
+ Assert.That(eventBus.Statistics!.GetListenerCount(nameof(TestEvent)), Is.EqualTo(0));
+ }
+
+ [Test]
+ public void WeakEvent_UnRegister_ShouldRemoveListener()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var listener = new EventListener();
+
+ var unregister = eventBus.RegisterWeak(listener.OnEvent);
+
+ // Act - 发送事件
+ eventBus.SendWeak(new TestEvent { Message = "Test1" });
+ Assert.That(listener.ReceivedCount, Is.EqualTo(1));
+
+ // 注销监听器
+ unregister.UnRegister();
+
+ // 再次发送事件
+ eventBus.SendWeak(new TestEvent { Message = "Test2" });
+
+ // Assert - 注销后不应该接收到事件
+ Assert.That(listener.ReceivedCount, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void WeakEvent_MultipleListeners_ShouldAllReceiveEvents()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus();
+ var listener1 = new EventListener();
+ var listener2 = new EventListener();
+ var listener3 = new EventListener();
+
+ eventBus.RegisterWeak(listener1.OnEvent);
+ eventBus.RegisterWeak(listener2.OnEvent);
+ eventBus.RegisterWeak(listener3.OnEvent);
+
+ // Act
+ eventBus.SendWeak(new TestEvent { Message = "Test" });
+
+ // Assert
+ Assert.That(listener1.ReceivedCount, Is.EqualTo(1));
+ Assert.That(listener2.ReceivedCount, Is.EqualTo(1));
+ Assert.That(listener3.ReceivedCount, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void WeakEvent_WithStatistics_ShouldTrackCorrectly()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+ var listener = new EventListener();
+
+ eventBus.RegisterWeak(listener.OnEvent);
+
+ // Act
+ eventBus.SendWeak(new TestEvent { Message = "Test1" });
+ eventBus.SendWeak(new TestEvent { Message = "Test2" });
+
+ // Assert
+ Assert.That(eventBus.Statistics!.TotalPublished, Is.EqualTo(2));
+ Assert.That(eventBus.Statistics.TotalHandled, Is.EqualTo(2));
+ Assert.That(eventBus.Statistics.GetListenerCount(nameof(TestEvent)), Is.EqualTo(1));
+ }
+
+ [Test]
+ public void WeakEvent_ExceptionInHandler_ShouldTrackFailure()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+
+ eventBus.RegisterWeak(_ => throw new InvalidOperationException("Test exception"));
+
+ // Act & Assert
+ Assert.Throws(() =>
+ eventBus.SendWeak(new TestEvent { Message = "Test" }));
+
+ Assert.That(eventBus.Statistics!.TotalFailed, Is.EqualTo(1));
+ }
+
+ [Test]
+ public void WeakEvent_AutoCleanupDuringTrigger_ShouldWork()
+ {
+ // Arrange
+ var eventBus = new EnhancedEventBus(enableStatistics: true);
+ var aliveListener = new EventListener();
+
+ void RegisterAndCollect()
+ {
+ var deadListener = new EventListener();
+ eventBus.RegisterWeak(deadListener.OnEvent);
+ }
+
+ RegisterAndCollect();
+ eventBus.RegisterWeak(aliveListener.OnEvent);
+
+ // 强制垃圾回收
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
+ // Act - 触发事件会自动清理已回收的监听器
+ eventBus.SendWeak(new TestEvent { Message = "Test" });
+
+ // Assert - 只有存活的监听器接收到事件
+ Assert.That(aliveListener.ReceivedCount, Is.EqualTo(1));
+ Assert.That(eventBus.Statistics!.GetListenerCount(nameof(TestEvent)), Is.EqualTo(1));
+ }
+
+ private sealed class TestEvent
+ {
+ public string Message { get; init; } = string.Empty;
+ }
+
+ private sealed class EventListener
+ {
+ public int ReceivedCount { get; private set; }
+
+ public void OnEvent(TestEvent e)
+ {
+ ReceivedCount++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/events/EnhancedEventBus.cs b/GFramework.Core/events/EnhancedEventBus.cs
new file mode 100644
index 0000000..248228b
--- /dev/null
+++ b/GFramework.Core/events/EnhancedEventBus.cs
@@ -0,0 +1,242 @@
+using System.Collections.Concurrent;
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events;
+
+///
+/// 增强的事件总线,支持统计和过滤器
+/// 线程安全:使用 ConcurrentDictionary 存储事件
+///
+public sealed class EnhancedEventBus : IEventBus
+{
+ private readonly EasyEvents _mEvents = new();
+ private readonly ConcurrentDictionary _mFilterableEvents = new();
+ private readonly EasyEvents _mPriorityEvents = new();
+ private readonly ConcurrentDictionary _mWeakEvents = new();
+ private readonly EventStatistics? _statistics;
+
+ ///
+ /// 构造函数
+ ///
+ /// 是否启用统计功能
+ public EnhancedEventBus(bool enableStatistics = false)
+ {
+ _statistics = enableStatistics ? new EventStatistics() : null;
+ }
+
+ ///
+ /// 获取事件统计信息(如果启用)
+ ///
+ public IEventStatistics? Statistics => _statistics;
+
+ #region IEventBus Implementation
+
+ ///
+ /// 发送指定类型的事件实例(自动创建默认实例)
+ ///
+ /// 事件类型
+ public void Send() where T : new()
+ {
+ _mEvents
+ .GetOrAddEvent>()
+ .Trigger(new T());
+ }
+
+ ///
+ /// 发送指定类型的事件实例
+ ///
+ /// 事件类型
+ /// 事件实例
+ public void Send(T e)
+ {
+ _mEvents
+ .GetOrAddEvent>()
+ .Trigger(e);
+ }
+
+ ///
+ /// 发送具有指定传播方式的优先级事件
+ ///
+ /// 事件类型
+ /// 事件实例
+ /// 事件传播方式
+ public void Send(T e, EventPropagation propagation)
+ {
+ _mPriorityEvents
+ .GetOrAddEvent>()
+ .Trigger(e, propagation);
+ }
+
+ ///
+ /// 注册事件监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调
+ /// 反注册接口,用于取消订阅
+ public IUnRegister Register(Action onEvent)
+ {
+ return _mEvents.GetOrAddEvent>().Register(onEvent);
+ }
+
+ ///
+ /// 注册具有优先级的监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调
+ /// 优先级,数值越大优先级越高
+ /// 反注册接口,用于取消订阅
+ public IUnRegister Register(Action onEvent, int priority)
+ {
+ return _mPriorityEvents.GetOrAddEvent>().Register(onEvent, priority);
+ }
+
+ ///
+ /// 注销事件监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调
+ public void UnRegister(Action onEvent)
+ {
+ _mEvents.GetEvent>().UnRegister(onEvent);
+ }
+
+ ///
+ /// 注册带有上下文信息的优先级事件监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调,接收事件上下文
+ /// 反注册接口,用于取消订阅
+ public IUnRegister RegisterWithContext(Action> onEvent)
+ {
+ return _mPriorityEvents.GetOrAddEvent>().RegisterWithContext(onEvent);
+ }
+
+ ///
+ /// 注册带有上下文信息和优先级的监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调,接收事件上下文
+ /// 优先级,数值越大优先级越高
+ /// 反注册接口,用于取消订阅
+ public IUnRegister RegisterWithContext(Action> onEvent, int priority)
+ {
+ return _mPriorityEvents.GetOrAddEvent>().RegisterWithContext(onEvent, priority);
+ }
+
+ #endregion
+
+ #region Filterable Events
+
+ ///
+ /// 发送支持过滤的事件
+ ///
+ /// 事件类型
+ /// 事件实例
+ public void SendFilterable(T e)
+ {
+ var evt = (FilterableEvent)_mFilterableEvents.GetOrAdd(
+ typeof(T),
+ _ => new FilterableEvent(_statistics));
+ evt.Trigger(e);
+ }
+
+ ///
+ /// 注册支持过滤的事件监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调
+ /// 反注册接口
+ public IUnRegister RegisterFilterable(Action onEvent)
+ {
+ var evt = (FilterableEvent)_mFilterableEvents.GetOrAdd(
+ typeof(T),
+ _ => new FilterableEvent(_statistics));
+ return evt.Register(onEvent);
+ }
+
+ ///
+ /// 为指定事件类型添加过滤器
+ ///
+ /// 事件类型
+ /// 过滤器
+ public void AddFilter(IEventFilter filter)
+ {
+ var evt = (FilterableEvent)_mFilterableEvents.GetOrAdd(
+ typeof(T),
+ _ => new FilterableEvent(_statistics));
+ evt.AddFilter(filter);
+ }
+
+ ///
+ /// 移除指定事件类型的过滤器
+ ///
+ /// 事件类型
+ /// 过滤器
+ public void RemoveFilter(IEventFilter filter)
+ {
+ if (_mFilterableEvents.TryGetValue(typeof(T), out var obj))
+ {
+ var evt = (FilterableEvent)obj;
+ evt.RemoveFilter(filter);
+ }
+ }
+
+ ///
+ /// 清除指定事件类型的所有过滤器
+ ///
+ /// 事件类型
+ public void ClearFilters()
+ {
+ if (_mFilterableEvents.TryGetValue(typeof(T), out var obj))
+ {
+ var evt = (FilterableEvent)obj;
+ evt.ClearFilters();
+ }
+ }
+
+ #endregion
+
+ #region Weak Events
+
+ ///
+ /// 发送弱引用事件
+ ///
+ /// 事件类型
+ /// 事件实例
+ public void SendWeak(T e)
+ {
+ var evt = (WeakEvent)_mWeakEvents.GetOrAdd(
+ typeof(T),
+ _ => new WeakEvent(_statistics));
+ evt.Trigger(e);
+ }
+
+ ///
+ /// 注册弱引用事件监听器
+ ///
+ /// 事件类型
+ /// 事件处理回调
+ /// 反注册接口
+ public IUnRegister RegisterWeak(Action onEvent)
+ {
+ var evt = (WeakEvent)_mWeakEvents.GetOrAdd(
+ typeof(T),
+ _ => new WeakEvent(_statistics));
+ return evt.Register(onEvent);
+ }
+
+ ///
+ /// 清理指定事件类型的已回收弱引用
+ ///
+ /// 事件类型
+ public void CleanupWeak()
+ {
+ if (_mWeakEvents.TryGetValue(typeof(T), out var obj))
+ {
+ var evt = (WeakEvent)obj;
+ evt.Cleanup();
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/GFramework.Core/events/EventStatistics.cs b/GFramework.Core/events/EventStatistics.cs
new file mode 100644
index 0000000..b1973d2
--- /dev/null
+++ b/GFramework.Core/events/EventStatistics.cs
@@ -0,0 +1,159 @@
+using System.Text;
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events;
+
+///
+/// 事件统计信息实现类
+/// 线程安全:使用 Interlocked 操作确保计数器的原子性
+///
+public sealed class EventStatistics : IEventStatistics
+{
+ private readonly Dictionary _listenerCountByType = new();
+ private readonly object _lock = new();
+ private readonly Dictionary _publishCountByType = new();
+ private int _activeEventTypes;
+ private int _activeListeners;
+ private long _totalFailed;
+ private long _totalHandled;
+ private long _totalPublished;
+
+ ///
+ public long TotalPublished => Interlocked.Read(ref _totalPublished);
+
+ ///
+ public long TotalHandled => Interlocked.Read(ref _totalHandled);
+
+ ///
+ public long TotalFailed => Interlocked.Read(ref _totalFailed);
+
+ ///
+ public int ActiveEventTypes
+ {
+ get => Interlocked.CompareExchange(ref _activeEventTypes, 0, 0);
+ set => Interlocked.Exchange(ref _activeEventTypes, value);
+ }
+
+ ///
+ public int ActiveListeners
+ {
+ get => Interlocked.CompareExchange(ref _activeListeners, 0, 0);
+ set => Interlocked.Exchange(ref _activeListeners, value);
+ }
+
+ ///
+ public long GetPublishCount(string eventType)
+ {
+ lock (_lock)
+ {
+ return _publishCountByType.TryGetValue(eventType, out var count) ? count : 0;
+ }
+ }
+
+ ///
+ public int GetListenerCount(string eventType)
+ {
+ lock (_lock)
+ {
+ return _listenerCountByType.TryGetValue(eventType, out var count) ? count : 0;
+ }
+ }
+
+ ///
+ public void Reset()
+ {
+ Interlocked.Exchange(ref _totalPublished, 0);
+ Interlocked.Exchange(ref _totalHandled, 0);
+ Interlocked.Exchange(ref _totalFailed, 0);
+ Interlocked.Exchange(ref _activeEventTypes, 0);
+ Interlocked.Exchange(ref _activeListeners, 0);
+
+ lock (_lock)
+ {
+ _publishCountByType.Clear();
+ _listenerCountByType.Clear();
+ }
+ }
+
+ ///
+ public string GenerateReport()
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("=== 事件统计报告 ===");
+ sb.AppendLine($"总发布数: {TotalPublished}");
+ sb.AppendLine($"总处理数: {TotalHandled}");
+ sb.AppendLine($"总失败数: {TotalFailed}");
+ sb.AppendLine($"活跃事件类型: {ActiveEventTypes}");
+ sb.AppendLine($"活跃监听器: {ActiveListeners}");
+
+ lock (_lock)
+ {
+ if (_publishCountByType.Count > 0)
+ {
+ sb.AppendLine("\n按事件类型统计(发布次数):");
+ foreach (var kvp in _publishCountByType.OrderByDescending(x => x.Value))
+ sb.AppendLine($" {kvp.Key}: {kvp.Value}");
+ }
+
+ if (_listenerCountByType.Count > 0)
+ {
+ sb.AppendLine("\n按事件类型统计(监听器数量):");
+ foreach (var kvp in _listenerCountByType.OrderByDescending(x => x.Value))
+ sb.AppendLine($" {kvp.Key}: {kvp.Value}");
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ ///
+ /// 记录事件发布
+ ///
+ /// 事件类型名称
+ public void RecordPublish(string eventType)
+ {
+ Interlocked.Increment(ref _totalPublished);
+
+ lock (_lock)
+ {
+ _publishCountByType.TryGetValue(eventType, out var count);
+ _publishCountByType[eventType] = count + 1;
+ }
+ }
+
+ ///
+ /// 记录事件处理
+ ///
+ public void RecordHandle()
+ {
+ Interlocked.Increment(ref _totalHandled);
+ }
+
+ ///
+ /// 记录事件处理失败
+ ///
+ public void RecordFailure()
+ {
+ Interlocked.Increment(ref _totalFailed);
+ }
+
+ ///
+ /// 更新事件类型的监听器数量
+ ///
+ /// 事件类型名称
+ /// 监听器数量
+ public void UpdateListenerCount(string eventType, int count)
+ {
+ lock (_lock)
+ {
+ if (count > 0)
+ {
+ _listenerCountByType[eventType] = count;
+ }
+ else
+ {
+ _listenerCountByType.Remove(eventType);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/events/FilterableEvent.cs b/GFramework.Core/events/FilterableEvent.cs
new file mode 100644
index 0000000..34e3cf1
--- /dev/null
+++ b/GFramework.Core/events/FilterableEvent.cs
@@ -0,0 +1,155 @@
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events;
+
+///
+/// 支持过滤器和统计的泛型事件类
+/// 线程安全:使用锁保护监听器列表和过滤器列表的修改
+///
+/// 事件数据类型
+public sealed class FilterableEvent
+{
+ private readonly List> _filters = new();
+ private readonly object _lock = new();
+ private readonly EventStatistics? _statistics;
+ private Action? _onEvent;
+
+ ///
+ /// 构造函数
+ ///
+ /// 事件统计对象(可选)
+ public FilterableEvent(EventStatistics? statistics = null)
+ {
+ _statistics = statistics;
+ }
+
+ ///
+ /// 注册事件监听器
+ ///
+ /// 事件处理回调
+ /// 反注册接口
+ public IUnRegister Register(Action onEvent)
+ {
+ lock (_lock)
+ {
+ _onEvent += onEvent;
+ UpdateListenerCount();
+ }
+
+ return new DefaultUnRegister(() => UnRegister(onEvent));
+ }
+
+ ///
+ /// 注销事件监听器
+ ///
+ /// 事件处理回调
+ public void UnRegister(Action onEvent)
+ {
+ lock (_lock)
+ {
+ _onEvent -= onEvent;
+ UpdateListenerCount();
+ }
+ }
+
+ ///
+ /// 触发事件
+ ///
+ /// 事件数据
+ public void Trigger(T data)
+ {
+ // 记录发布统计
+ _statistics?.RecordPublish(typeof(T).Name);
+
+ // 应用过滤器
+ lock (_lock)
+ {
+ // 事件被过滤,不触发监听器
+ if (_filters.Any(filter => filter.ShouldFilter(data)))
+ return;
+ }
+
+ // 获取当前监听器快照(在锁外调用)
+ Action? handlers;
+ lock (_lock)
+ {
+ handlers = _onEvent;
+ }
+
+ if (handlers == null)
+ return;
+
+ // 在锁外调用监听器,避免死锁
+ foreach (var handler in handlers.GetInvocationList().Cast>())
+ {
+ try
+ {
+ handler(data);
+ _statistics?.RecordHandle();
+ }
+ catch
+ {
+ _statistics?.RecordFailure();
+ throw;
+ }
+ }
+ }
+
+ ///
+ /// 添加事件过滤器
+ ///
+ /// 过滤器
+ public void AddFilter(IEventFilter filter)
+ {
+ lock (_lock)
+ {
+ _filters.Add(filter);
+ }
+ }
+
+ ///
+ /// 移除事件过滤器
+ ///
+ /// 过滤器
+ public void RemoveFilter(IEventFilter filter)
+ {
+ lock (_lock)
+ {
+ _filters.Remove(filter);
+ }
+ }
+
+ ///
+ /// 清除所有过滤器
+ ///
+ public void ClearFilters()
+ {
+ lock (_lock)
+ {
+ _filters.Clear();
+ }
+ }
+
+ ///
+ /// 获取当前监听器数量
+ ///
+ public int GetListenerCount()
+ {
+ lock (_lock)
+ {
+ return _onEvent?.GetInvocationList().Length ?? 0;
+ }
+ }
+
+ ///
+ /// 更新监听器数量统计
+ ///
+ private void UpdateListenerCount()
+ {
+ if (_statistics == null)
+ return;
+
+ var count = _onEvent?.GetInvocationList().Length ?? 0;
+ _statistics.UpdateListenerCount(typeof(T).Name, count);
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/events/WeakEvent.cs b/GFramework.Core/events/WeakEvent.cs
new file mode 100644
index 0000000..2d55da0
--- /dev/null
+++ b/GFramework.Core/events/WeakEvent.cs
@@ -0,0 +1,154 @@
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events;
+
+///
+/// 支持弱引用订阅的泛型事件类
+/// 使用弱引用存储监听器,避免事件订阅导致的内存泄漏
+/// 线程安全:使用锁保护监听器列表的修改
+///
+/// 事件数据类型
+public sealed class WeakEvent
+{
+ private readonly object _lock = new();
+ private readonly EventStatistics? _statistics;
+ private readonly List>> _weakHandlers = new();
+
+ ///
+ /// 构造函数
+ ///
+ /// 事件统计对象(可选)
+ public WeakEvent(EventStatistics? statistics = null)
+ {
+ _statistics = statistics;
+ }
+
+ ///
+ /// 注册事件监听器(弱引用)
+ ///
+ /// 事件处理回调
+ /// 反注册接口
+ public IUnRegister Register(Action onEvent)
+ {
+ lock (_lock)
+ {
+ _weakHandlers.Add(new WeakReference>(onEvent));
+ UpdateListenerCount();
+ }
+
+ return new DefaultUnRegister(() => UnRegister(onEvent));
+ }
+
+ ///
+ /// 注销事件监听器
+ ///
+ /// 事件处理回调
+ public void UnRegister(Action onEvent)
+ {
+ lock (_lock)
+ {
+ _weakHandlers.RemoveAll(wr =>
+ {
+ if (!wr.TryGetTarget(out var target))
+ return true; // 目标已被回收,移除
+ return ReferenceEquals(target, onEvent);
+ });
+ UpdateListenerCount();
+ }
+ }
+
+ ///
+ /// 触发事件
+ ///
+ /// 事件数据
+ public void Trigger(T data)
+ {
+ // 记录发布统计
+ _statistics?.RecordPublish(typeof(T).Name);
+
+ // 收集存活的监听器
+ var aliveHandlers = new List>();
+ var needsUpdate = false;
+
+ lock (_lock)
+ {
+ var beforeCount = _weakHandlers.Count;
+
+ // 清理已回收的弱引用并收集存活的监听器
+ _weakHandlers.RemoveAll(wr =>
+ {
+ if (wr.TryGetTarget(out var target))
+ {
+ aliveHandlers.Add(target);
+ return false;
+ }
+
+ return true; // 目标已被回收,移除
+ });
+
+ // 检查是否有监听器被清理
+ needsUpdate = _weakHandlers.Count != beforeCount;
+ }
+
+ // 在锁外调用监听器,避免死锁
+ foreach (var handler in aliveHandlers)
+ {
+ try
+ {
+ handler(data);
+ _statistics?.RecordHandle();
+ }
+ catch
+ {
+ _statistics?.RecordFailure();
+ throw;
+ }
+ }
+
+ // 如果有监听器被清理,更新统计
+ if (needsUpdate)
+ {
+ lock (_lock)
+ {
+ UpdateListenerCount();
+ }
+ }
+ }
+
+ ///
+ /// 清理已回收的弱引用
+ ///
+ public void Cleanup()
+ {
+ lock (_lock)
+ {
+ var beforeCount = _weakHandlers.Count;
+ _weakHandlers.RemoveAll(wr => !wr.TryGetTarget(out _));
+ if (_weakHandlers.Count != beforeCount)
+ UpdateListenerCount();
+ }
+ }
+
+ ///
+ /// 获取当前存活的监听器数量
+ ///
+ public int GetListenerCount()
+ {
+ lock (_lock)
+ {
+ return _weakHandlers.Count(wr => wr.TryGetTarget(out _));
+ }
+ }
+
+ ///
+ /// 更新监听器数量统计
+ ///
+ private void UpdateListenerCount()
+ {
+ if (_statistics == null)
+ return;
+
+ var count = _weakHandlers.Count(wr => wr.TryGetTarget(out _));
+ _statistics.UpdateListenerCount(typeof(T).Name, count);
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/events/filters/PredicateEventFilter.cs b/GFramework.Core/events/filters/PredicateEventFilter.cs
new file mode 100644
index 0000000..de533c4
--- /dev/null
+++ b/GFramework.Core/events/filters/PredicateEventFilter.cs
@@ -0,0 +1,28 @@
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events.filters;
+
+///
+/// 基于谓词的事件过滤器
+/// 允许使用自定义条件函数来过滤事件
+///
+/// 事件类型
+public sealed class PredicateEventFilter : IEventFilter
+{
+ private readonly Func _predicate;
+
+ ///
+ /// 构造函数
+ ///
+ /// 过滤条件函数,返回 true 表示过滤(阻止),返回 false 表示允许
+ public PredicateEventFilter(Func predicate)
+ {
+ _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
+ }
+
+ ///
+ public bool ShouldFilter(T eventData)
+ {
+ return _predicate(eventData);
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core/events/filters/SamplingEventFilter.cs b/GFramework.Core/events/filters/SamplingEventFilter.cs
new file mode 100644
index 0000000..76fdb51
--- /dev/null
+++ b/GFramework.Core/events/filters/SamplingEventFilter.cs
@@ -0,0 +1,40 @@
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events.filters;
+
+///
+/// 采样事件过滤器
+/// 按照指定的采样率过滤事件,用于限制高频事件的处理
+///
+/// 事件类型
+public sealed class SamplingEventFilter : IEventFilter
+{
+ private readonly double _samplingRate;
+ private long _counter;
+
+ ///
+ /// 构造函数
+ ///
+ /// 采样率,范围 0.0 到 1.0。例如 0.1 表示只允许 10% 的事件通过
+ public SamplingEventFilter(double samplingRate)
+ {
+ if (samplingRate < 0.0 || samplingRate > 1.0)
+ throw new ArgumentOutOfRangeException(nameof(samplingRate), "采样率必须在 0.0 到 1.0 之间");
+
+ _samplingRate = samplingRate;
+ }
+
+ ///
+ public bool ShouldFilter(T eventData)
+ {
+ if (_samplingRate >= 1.0)
+ return false; // 采样率 100%,不过滤
+
+ if (_samplingRate <= 0.0)
+ return true; // 采样率 0%,全部过滤
+
+ var count = Interlocked.Increment(ref _counter);
+ var threshold = (long)(1.0 / _samplingRate);
+ return count % threshold != 0;
+ }
+}
\ No newline at end of file
From 5ef464c484cda2afb961858c5450cd2ac1833a05 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 6 Mar 2026 16:40:54 +0800
Subject: [PATCH 2/3] =?UTF-8?q?feat(event):=20=E6=B7=BB=E5=8A=A0=E4=BA=8B?=
=?UTF-8?q?=E4=BB=B6=E7=BB=9F=E8=AE=A1=E5=8A=9F=E8=83=BD=E5=92=8C=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E6=80=A7=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在EasyEventGeneric中添加GetListenerCount方法获取监听器数量
- 重新排列EnhancedEventBus中字段顺序以优化内存布局
- 为所有Send方法添加发布统计记录功能
- 实现事件处理器包装以添加统计功能,包括成功处理和失败记录
- 添加监听器数量统计更新机制,自动跟踪注册和注销事件
- 重构FilterableEvent中的过滤逻辑以减少锁持有时间
- 在PriorityEvent中添加GetListenerCount方法获取总监听器数量
- 移除EventStatistics中的过时字段并优化ActiveEventTypes和ActiveListeners计算
- 清理StatisticsEventDecorator中的并发问题和实现装饰器模式
---
GFramework.Core/events/EasyEventGeneric.cs | 18 +++
GFramework.Core/events/EnhancedEventBus.cs | 148 ++++++++++++++++++
GFramework.Core/events/EventStatistics.cs | 22 ++-
GFramework.Core/events/FilterableEvent.cs | 18 +--
GFramework.Core/events/PriorityEvent.cs | 9 ++
.../events/StatisticsEventDecorator.cs | 108 +++++++++++++
6 files changed, 306 insertions(+), 17 deletions(-)
create mode 100644 GFramework.Core/events/StatisticsEventDecorator.cs
diff --git a/GFramework.Core/events/EasyEventGeneric.cs b/GFramework.Core/events/EasyEventGeneric.cs
index 002678c..e1fbffb 100644
--- a/GFramework.Core/events/EasyEventGeneric.cs
+++ b/GFramework.Core/events/EasyEventGeneric.cs
@@ -59,6 +59,15 @@ public class Event : IEvent
{
_mOnEvent?.Invoke(t);
}
+
+ ///
+ /// 获取当前已注册的监听器数量
+ ///
+ /// 监听器数量
+ public int GetListenerCount()
+ {
+ return _mOnEvent?.GetInvocationList().Length ?? 0;
+ }
}
///
@@ -120,4 +129,13 @@ public class Event : IEvent
{
_mOnEvent?.Invoke(t, k);
}
+
+ ///
+ /// 获取当前已注册的监听器数量
+ ///
+ /// 监听器数量
+ public int GetListenerCount()
+ {
+ return _mOnEvent?.GetInvocationList().Length ?? 0;
+ }
}
\ No newline at end of file
diff --git a/GFramework.Core/events/EnhancedEventBus.cs b/GFramework.Core/events/EnhancedEventBus.cs
index 248228b..dd7d752 100644
--- a/GFramework.Core/events/EnhancedEventBus.cs
+++ b/GFramework.Core/events/EnhancedEventBus.cs
@@ -37,6 +37,8 @@ public sealed class EnhancedEventBus : IEventBus
/// 事件类型
public void Send() where T : new()
{
+ _statistics?.RecordPublish(typeof(T).Name);
+
_mEvents
.GetOrAddEvent>()
.Trigger(new T());
@@ -49,6 +51,8 @@ public sealed class EnhancedEventBus : IEventBus
/// 事件实例
public void Send(T e)
{
+ _statistics?.RecordPublish(typeof(T).Name);
+
_mEvents
.GetOrAddEvent>()
.Trigger(e);
@@ -62,6 +66,8 @@ public sealed class EnhancedEventBus : IEventBus
/// 事件传播方式
public void Send(T e, EventPropagation propagation)
{
+ _statistics?.RecordPublish(typeof(T).Name);
+
_mPriorityEvents
.GetOrAddEvent>()
.Trigger(e, propagation);
@@ -75,6 +81,32 @@ public sealed class EnhancedEventBus : IEventBus
/// 反注册接口,用于取消订阅
public IUnRegister Register(Action onEvent)
{
+ if (_statistics != null)
+ {
+ // 包装回调以添加统计
+ Action wrappedHandler = data =>
+ {
+ try
+ {
+ onEvent(data);
+ _statistics.RecordHandle();
+ }
+ catch
+ {
+ _statistics.RecordFailure();
+ throw;
+ }
+ };
+
+ var unregister = _mEvents.GetOrAddEvent>().Register(wrappedHandler);
+ UpdateEventListenerCount();
+ return new DefaultUnRegister(() =>
+ {
+ unregister.UnRegister();
+ UpdateEventListenerCount();
+ });
+ }
+
return _mEvents.GetOrAddEvent>().Register(onEvent);
}
@@ -87,6 +119,32 @@ public sealed class EnhancedEventBus : IEventBus
/// 反注册接口,用于取消订阅
public IUnRegister Register(Action onEvent, int priority)
{
+ if (_statistics != null)
+ {
+ // 包装回调以添加统计
+ Action wrappedHandler = data =>
+ {
+ try
+ {
+ onEvent(data);
+ _statistics.RecordHandle();
+ }
+ catch
+ {
+ _statistics.RecordFailure();
+ throw;
+ }
+ };
+
+ var unregister = _mPriorityEvents.GetOrAddEvent>().Register(wrappedHandler, priority);
+ UpdatePriorityEventListenerCount();
+ return new DefaultUnRegister(() =>
+ {
+ unregister.UnRegister();
+ UpdatePriorityEventListenerCount();
+ });
+ }
+
return _mPriorityEvents.GetOrAddEvent>().Register(onEvent, priority);
}
@@ -98,6 +156,7 @@ public sealed class EnhancedEventBus : IEventBus
public void UnRegister(Action onEvent)
{
_mEvents.GetEvent>().UnRegister(onEvent);
+ UpdateEventListenerCount();
}
///
@@ -108,6 +167,32 @@ public sealed class EnhancedEventBus : IEventBus
/// 反注册接口,用于取消订阅
public IUnRegister RegisterWithContext(Action> onEvent)
{
+ if (_statistics != null)
+ {
+ // 包装回调以添加统计
+ Action> wrappedHandler = context =>
+ {
+ try
+ {
+ onEvent(context);
+ _statistics.RecordHandle();
+ }
+ catch
+ {
+ _statistics.RecordFailure();
+ throw;
+ }
+ };
+
+ var unregister = _mPriorityEvents.GetOrAddEvent>().RegisterWithContext(wrappedHandler);
+ UpdatePriorityEventListenerCount();
+ return new DefaultUnRegister(() =>
+ {
+ unregister.UnRegister();
+ UpdatePriorityEventListenerCount();
+ });
+ }
+
return _mPriorityEvents.GetOrAddEvent>().RegisterWithContext(onEvent);
}
@@ -120,6 +205,33 @@ public sealed class EnhancedEventBus : IEventBus
/// 反注册接口,用于取消订阅
public IUnRegister RegisterWithContext(Action> onEvent, int priority)
{
+ if (_statistics != null)
+ {
+ // 包装回调以添加统计
+ Action> wrappedHandler = context =>
+ {
+ try
+ {
+ onEvent(context);
+ _statistics.RecordHandle();
+ }
+ catch
+ {
+ _statistics.RecordFailure();
+ throw;
+ }
+ };
+
+ var unregister = _mPriorityEvents.GetOrAddEvent>()
+ .RegisterWithContext(wrappedHandler, priority);
+ UpdatePriorityEventListenerCount();
+ return new DefaultUnRegister(() =>
+ {
+ unregister.UnRegister();
+ UpdatePriorityEventListenerCount();
+ });
+ }
+
return _mPriorityEvents.GetOrAddEvent>().RegisterWithContext(onEvent, priority);
}
@@ -239,4 +351,40 @@ public sealed class EnhancedEventBus : IEventBus
}
#endregion
+
+ #region Helper Methods
+
+ ///
+ /// 更新普通事件的监听器数量统计
+ ///
+ private void UpdateEventListenerCount()
+ {
+ if (_statistics == null)
+ return;
+
+ var evt = _mEvents.GetEvent>();
+ if (evt != null)
+ {
+ var count = evt.GetListenerCount();
+ _statistics.UpdateListenerCount(typeof(T).Name, count);
+ }
+ }
+
+ ///
+ /// 更新优先级事件的监听器数量统计
+ ///
+ private void UpdatePriorityEventListenerCount()
+ {
+ if (_statistics == null)
+ return;
+
+ var evt = _mPriorityEvents.GetEvent>();
+ if (evt != null)
+ {
+ var count = evt.GetListenerCount();
+ _statistics.UpdateListenerCount(typeof(T).Name, count);
+ }
+ }
+
+ #endregion
}
\ No newline at end of file
diff --git a/GFramework.Core/events/EventStatistics.cs b/GFramework.Core/events/EventStatistics.cs
index b1973d2..4f7d447 100644
--- a/GFramework.Core/events/EventStatistics.cs
+++ b/GFramework.Core/events/EventStatistics.cs
@@ -12,8 +12,6 @@ public sealed class EventStatistics : IEventStatistics
private readonly Dictionary _listenerCountByType = new();
private readonly object _lock = new();
private readonly Dictionary _publishCountByType = new();
- private int _activeEventTypes;
- private int _activeListeners;
private long _totalFailed;
private long _totalHandled;
private long _totalPublished;
@@ -30,15 +28,25 @@ public sealed class EventStatistics : IEventStatistics
///
public int ActiveEventTypes
{
- get => Interlocked.CompareExchange(ref _activeEventTypes, 0, 0);
- set => Interlocked.Exchange(ref _activeEventTypes, value);
+ get
+ {
+ lock (_lock)
+ {
+ return _publishCountByType.Count;
+ }
+ }
}
///
public int ActiveListeners
{
- get => Interlocked.CompareExchange(ref _activeListeners, 0, 0);
- set => Interlocked.Exchange(ref _activeListeners, value);
+ get
+ {
+ lock (_lock)
+ {
+ return _listenerCountByType.Values.Sum();
+ }
+ }
}
///
@@ -65,8 +73,6 @@ public sealed class EventStatistics : IEventStatistics
Interlocked.Exchange(ref _totalPublished, 0);
Interlocked.Exchange(ref _totalHandled, 0);
Interlocked.Exchange(ref _totalFailed, 0);
- Interlocked.Exchange(ref _activeEventTypes, 0);
- Interlocked.Exchange(ref _activeListeners, 0);
lock (_lock)
{
diff --git a/GFramework.Core/events/FilterableEvent.cs b/GFramework.Core/events/FilterableEvent.cs
index 34e3cf1..406340f 100644
--- a/GFramework.Core/events/FilterableEvent.cs
+++ b/GFramework.Core/events/FilterableEvent.cs
@@ -61,21 +61,21 @@ public sealed class FilterableEvent
// 记录发布统计
_statistics?.RecordPublish(typeof(T).Name);
- // 应用过滤器
- lock (_lock)
- {
- // 事件被过滤,不触发监听器
- if (_filters.Any(filter => filter.ShouldFilter(data)))
- return;
- }
-
- // 获取当前监听器快照(在锁外调用)
+ // 在单个锁中快照过滤器和监听器
Action? handlers;
+ IEventFilter[] filtersSnapshot;
+
lock (_lock)
{
+ filtersSnapshot = _filters.Count > 0 ? _filters.ToArray() : Array.Empty>();
handlers = _onEvent;
}
+ // 在锁外执行过滤逻辑
+ // 事件被过滤,不触发监听器
+ if (filtersSnapshot.Any(filter => filter.ShouldFilter(data)))
+ return;
+
if (handlers == null)
return;
diff --git a/GFramework.Core/events/PriorityEvent.cs b/GFramework.Core/events/PriorityEvent.cs
index 5be3376..85b054b 100644
--- a/GFramework.Core/events/PriorityEvent.cs
+++ b/GFramework.Core/events/PriorityEvent.cs
@@ -254,6 +254,15 @@ public class PriorityEvent : IEvent
}
}
+ ///
+ /// 获取当前已注册的监听器总数量(包括普通监听器和上下文监听器)
+ ///
+ /// 监听器总数量
+ public int GetListenerCount()
+ {
+ return _handlers.Count + _contextHandlers.Count;
+ }
+
///
/// 事件处理器包装类,包含处理器和优先级
///
diff --git a/GFramework.Core/events/StatisticsEventDecorator.cs b/GFramework.Core/events/StatisticsEventDecorator.cs
new file mode 100644
index 0000000..ac9e3a5
--- /dev/null
+++ b/GFramework.Core/events/StatisticsEventDecorator.cs
@@ -0,0 +1,108 @@
+using GFramework.Core.Abstractions.events;
+
+namespace GFramework.Core.events;
+
+///
+/// 带统计功能的事件装饰器
+/// 使用装饰器模式为任何 IEvent 实现添加统计功能
+///
+/// 事件数据类型
+internal sealed class StatisticsEventDecorator
+{
+ private readonly string _eventTypeName;
+ private readonly IEvent _innerEvent;
+ private readonly EventStatistics _statistics;
+
+ ///
+ /// 构造函数
+ ///
+ /// 被装饰的事件对象
+ /// 统计对象
+ public StatisticsEventDecorator(IEvent innerEvent, EventStatistics statistics)
+ {
+ _innerEvent = innerEvent ?? throw new ArgumentNullException(nameof(innerEvent));
+ _statistics = statistics ?? throw new ArgumentNullException(nameof(statistics));
+ _eventTypeName = typeof(T).Name;
+ }
+
+ ///
+ /// 注册事件监听器(带统计)
+ ///
+ /// 事件处理回调
+ /// 反注册接口
+ public IUnRegister Register(Action onEvent)
+ {
+ // 包装回调以添加统计
+ Action wrappedHandler = data =>
+ {
+ try
+ {
+ onEvent(data);
+ _statistics.RecordHandle();
+ }
+ catch
+ {
+ _statistics.RecordFailure();
+ throw;
+ }
+ };
+
+ var unregister = _innerEvent.Register(() => { }); // 占位,实际不使用
+
+ // 直接注册到内部事件
+ if (_innerEvent is Event typedEvent)
+ {
+ unregister = typedEvent.Register(wrappedHandler);
+ }
+ else if (_innerEvent is PriorityEvent priorityEvent)
+ {
+ unregister = priorityEvent.Register(wrappedHandler);
+ }
+
+ // 更新监听器统计
+ UpdateListenerCount();
+
+ return new DefaultUnRegister(() =>
+ {
+ unregister.UnRegister();
+ UpdateListenerCount();
+ });
+ }
+
+ ///
+ /// 触发事件(带统计)
+ ///
+ /// 事件数据
+ public void Trigger(T data)
+ {
+ _statistics.RecordPublish(_eventTypeName);
+
+ if (_innerEvent is Event typedEvent)
+ {
+ typedEvent.Trigger(data);
+ }
+ else if (_innerEvent is PriorityEvent priorityEvent)
+ {
+ priorityEvent.Trigger(data, EventPropagation.Continue);
+ }
+ }
+
+ ///
+ /// 更新监听器数量统计
+ ///
+ private void UpdateListenerCount()
+ {
+ var count = 0;
+
+ if (_innerEvent is Event typedEvent)
+ {
+ count = typedEvent.GetListenerCount();
+ }
+ else if (_innerEvent is PriorityEvent priorityEvent)
+ {
+ count = priorityEvent.GetListenerCount();
+ }
+
+ _statistics.UpdateListenerCount(_eventTypeName, count);
+ }
+}
\ No newline at end of file
From c5bd08acfd7614ee1016823aa2d496369a1fccbc Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 6 Mar 2026 16:58:27 +0800
Subject: [PATCH 3/3] =?UTF-8?q?fix(events):=20=E4=BF=AE=E6=AD=A3=E4=BC=98?=
=?UTF-8?q?=E5=85=88=E7=BA=A7=E4=BA=8B=E4=BB=B6=E8=A7=A6=E5=8F=91=E4=BC=A0?=
=?UTF-8?q?=E6=92=AD=E8=A1=8C=E4=B8=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将优先级事件的传播模式从 Continue 修改为 All
- 确保事件能够正确传递到所有监听器
---
GFramework.Core/events/StatisticsEventDecorator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/GFramework.Core/events/StatisticsEventDecorator.cs b/GFramework.Core/events/StatisticsEventDecorator.cs
index ac9e3a5..41bdf85 100644
--- a/GFramework.Core/events/StatisticsEventDecorator.cs
+++ b/GFramework.Core/events/StatisticsEventDecorator.cs
@@ -83,7 +83,7 @@ internal sealed class StatisticsEventDecorator
}
else if (_innerEvent is PriorityEvent priorityEvent)
{
- priorityEvent.Trigger(data, EventPropagation.Continue);
+ priorityEvent.Trigger(data, EventPropagation.All);
}
}