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/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 new file mode 100644 index 0000000..dd7d752 --- /dev/null +++ b/GFramework.Core/events/EnhancedEventBus.cs @@ -0,0 +1,390 @@ +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() + { + _statistics?.RecordPublish(typeof(T).Name); + + _mEvents + .GetOrAddEvent>() + .Trigger(new T()); + } + + /// + /// 发送指定类型的事件实例 + /// + /// 事件类型 + /// 事件实例 + public void Send(T e) + { + _statistics?.RecordPublish(typeof(T).Name); + + _mEvents + .GetOrAddEvent>() + .Trigger(e); + } + + /// + /// 发送具有指定传播方式的优先级事件 + /// + /// 事件类型 + /// 事件实例 + /// 事件传播方式 + public void Send(T e, EventPropagation propagation) + { + _statistics?.RecordPublish(typeof(T).Name); + + _mPriorityEvents + .GetOrAddEvent>() + .Trigger(e, propagation); + } + + /// + /// 注册事件监听器 + /// + /// 事件类型 + /// 事件处理回调 + /// 反注册接口,用于取消订阅 + 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); + } + + /// + /// 注册具有优先级的监听器 + /// + /// 事件类型 + /// 事件处理回调 + /// 优先级,数值越大优先级越高 + /// 反注册接口,用于取消订阅 + 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); + } + + /// + /// 注销事件监听器 + /// + /// 事件类型 + /// 事件处理回调 + public void UnRegister(Action onEvent) + { + _mEvents.GetEvent>().UnRegister(onEvent); + UpdateEventListenerCount(); + } + + /// + /// 注册带有上下文信息的优先级事件监听器 + /// + /// 事件类型 + /// 事件处理回调,接收事件上下文 + /// 反注册接口,用于取消订阅 + 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); + } + + /// + /// 注册带有上下文信息和优先级的监听器 + /// + /// 事件类型 + /// 事件处理回调,接收事件上下文 + /// 优先级,数值越大优先级越高 + /// 反注册接口,用于取消订阅 + 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); + } + + #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 + + #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 new file mode 100644 index 0000000..4f7d447 --- /dev/null +++ b/GFramework.Core/events/EventStatistics.cs @@ -0,0 +1,165 @@ +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 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 + { + lock (_lock) + { + return _publishCountByType.Count; + } + } + } + + /// + public int ActiveListeners + { + get + { + lock (_lock) + { + return _listenerCountByType.Values.Sum(); + } + } + } + + /// + 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); + + 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..406340f --- /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); + + // 在单个锁中快照过滤器和监听器 + 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; + + // 在锁外调用监听器,避免死锁 + 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/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..41bdf85 --- /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.All); + } + } + + /// + /// 更新监听器数量统计 + /// + 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 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