mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
Merge pull request #83 from GeWuYou/feat/events-enhanced-bus
引入增强版事件总线,支持可过滤事件与弱事件,并可选地对事件发布与处理进行统计。 新特性: 新增 EnhancedEventBus,支持标准、优先级、可过滤和弱事件的发布与订阅,并提供可选的统计功能。 引入可过滤事件,支持可插拔的 IEventFilter 实现,包括基于谓词的过滤器和采样过滤器。 新增事件统计抽象及实现,用于跟踪发布/处理/失败计数和监听器指标,并支持生成统计报告。 测试: 新增全面的单元测试覆盖弱事件行为,包括与 GC 的交互、清理、注销以及与统计功能的集成。 新增基于谓词和采样事件过滤器的测试,包括多个过滤器、移除/清空行为以及对无效配置的校验。 新增事件统计测试,用于验证计数器、按事件类型的指标、重置行为、报告输出以及线程安全性。
This commit is contained in:
commit
23f70a6e5a
16
GFramework.Core.Abstractions/events/IEventFilter.cs
Normal file
16
GFramework.Core.Abstractions/events/IEventFilter.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件过滤器接口
|
||||||
|
/// 用于在事件触发前进行条件判断,决定是否允许事件传递给监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
public interface IEventFilter<in T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 判断事件是否应该被过滤(阻止传递)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventData">事件数据</param>
|
||||||
|
/// <returns>如果返回 true,则事件被过滤(不传递给监听器);如果返回 false,则允许传递</returns>
|
||||||
|
bool ShouldFilter(T eventData);
|
||||||
|
}
|
||||||
58
GFramework.Core.Abstractions/events/IEventStatistics.cs
Normal file
58
GFramework.Core.Abstractions/events/IEventStatistics.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
namespace GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件统计信息接口
|
||||||
|
/// 提供事件系统的性能统计数据
|
||||||
|
/// </summary>
|
||||||
|
public interface IEventStatistics
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取总事件发布数量
|
||||||
|
/// </summary>
|
||||||
|
long TotalPublished { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取总事件处理数量(监听器调用次数)
|
||||||
|
/// </summary>
|
||||||
|
long TotalHandled { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取总事件处理失败数量
|
||||||
|
/// </summary>
|
||||||
|
long TotalFailed { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前活跃的事件类型数量
|
||||||
|
/// </summary>
|
||||||
|
int ActiveEventTypes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前活跃的监听器总数
|
||||||
|
/// </summary>
|
||||||
|
int ActiveListeners { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定事件类型的发布次数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType">事件类型名称</param>
|
||||||
|
/// <returns>发布次数</returns>
|
||||||
|
long GetPublishCount(string eventType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定事件类型的监听器数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType">事件类型名称</param>
|
||||||
|
/// <returns>监听器数量</returns>
|
||||||
|
int GetListenerCount(string eventType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重置统计数据
|
||||||
|
/// </summary>
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成统计报告
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>格式化的统计报告字符串</returns>
|
||||||
|
string GenerateReport();
|
||||||
|
}
|
||||||
188
GFramework.Core.Tests/events/EventFilterTests.cs
Normal file
188
GFramework.Core.Tests/events/EventFilterTests.cs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
using GFramework.Core.events;
|
||||||
|
using GFramework.Core.events.filters;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件过滤器功能测试
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EventFilterTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void PredicateFilter_ShouldFilterBasedOnCondition()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EnhancedEventBus();
|
||||||
|
var receivedEvents = new List<TestEvent>();
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(e => receivedEvents.Add(e));
|
||||||
|
eventBus.AddFilter(new PredicateEventFilter<TestEvent>(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<TestEvent>();
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(e => receivedEvents.Add(e));
|
||||||
|
eventBus.AddFilter(new SamplingEventFilter<TestEvent>(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<TestEvent>();
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(e => receivedEvents.Add(e));
|
||||||
|
eventBus.AddFilter(new SamplingEventFilter<TestEvent>(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<TestEvent>();
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(e => receivedEvents.Add(e));
|
||||||
|
eventBus.AddFilter(new SamplingEventFilter<TestEvent>(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<TestEvent>();
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(e => receivedEvents.Add(e));
|
||||||
|
eventBus.AddFilter(new PredicateEventFilter<TestEvent>(e => e.Value < 10)); // 过滤 < 10
|
||||||
|
eventBus.AddFilter(new PredicateEventFilter<TestEvent>(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<TestEvent>();
|
||||||
|
var filter = new PredicateEventFilter<TestEvent>(e => e.Value < 10);
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(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<TestEvent>();
|
||||||
|
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(e => receivedEvents.Add(e));
|
||||||
|
eventBus.AddFilter(new PredicateEventFilter<TestEvent>(e => e.Value < 10));
|
||||||
|
eventBus.AddFilter(new PredicateEventFilter<TestEvent>(e => e.Value > 50));
|
||||||
|
|
||||||
|
// Act - 发送事件(应该被过滤)
|
||||||
|
eventBus.SendFilterable(new TestEvent { Value = 5 });
|
||||||
|
Assert.That(receivedEvents, Is.Empty);
|
||||||
|
|
||||||
|
// 清除所有过滤器
|
||||||
|
eventBus.ClearFilters<TestEvent>();
|
||||||
|
|
||||||
|
// 再次发送事件(应该通过)
|
||||||
|
eventBus.SendFilterable(new TestEvent { Value = 5 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(receivedEvents, Has.Count.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SamplingFilter_InvalidRate_ShouldThrowException()
|
||||||
|
{
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||||
|
new SamplingEventFilter<TestEvent>(-0.1));
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||||
|
new SamplingEventFilter<TestEvent>(1.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PredicateFilter_NullPredicate_ShouldThrowException()
|
||||||
|
{
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentNullException>(() =>
|
||||||
|
new PredicateEventFilter<TestEvent>(null!));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TestEvent
|
||||||
|
{
|
||||||
|
public int Value { get; init; }
|
||||||
|
public string Message { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
176
GFramework.Core.Tests/events/EventStatisticsTests.cs
Normal file
176
GFramework.Core.Tests/events/EventStatisticsTests.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using GFramework.Core.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件统计功能测试
|
||||||
|
/// </summary>
|
||||||
|
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<TestEvent>(_ => handledCount++);
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(_ => 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<TestEvent>(_ => throw new InvalidOperationException("Test exception"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
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<TestEvent>(_ => { });
|
||||||
|
eventBus.RegisterFilterable<TestEvent>(_ => { });
|
||||||
|
|
||||||
|
// 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<TestEvent>(_ => { });
|
||||||
|
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<TestEvent>(_ => { });
|
||||||
|
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<TestEvent>(_ => { });
|
||||||
|
|
||||||
|
// Act - 并发发送事件
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
206
GFramework.Core.Tests/events/WeakEventTests.cs
Normal file
206
GFramework.Core.Tests/events/WeakEventTests.cs
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
using GFramework.Core.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弱引用事件功能测试
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeakEventTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void WeakEvent_ShouldReceiveEvents()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EnhancedEventBus();
|
||||||
|
var listener = new EventListener();
|
||||||
|
|
||||||
|
eventBus.RegisterWeak<TestEvent>(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<TestEvent>(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<TestEvent>(listener.OnEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterAndCollect();
|
||||||
|
|
||||||
|
// 强制垃圾回收
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
GC.Collect();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
eventBus.CleanupWeak<TestEvent>();
|
||||||
|
|
||||||
|
// 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<TestEvent>(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<TestEvent>(listener1.OnEvent);
|
||||||
|
eventBus.RegisterWeak<TestEvent>(listener2.OnEvent);
|
||||||
|
eventBus.RegisterWeak<TestEvent>(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<TestEvent>(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<TestEvent>(_ => throw new InvalidOperationException("Test exception"));
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
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<TestEvent>(deadListener.OnEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterAndCollect();
|
||||||
|
eventBus.RegisterWeak<TestEvent>(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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -59,6 +59,15 @@ public class Event<T> : IEvent
|
|||||||
{
|
{
|
||||||
_mOnEvent?.Invoke(t);
|
_mOnEvent?.Invoke(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前已注册的监听器数量
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>监听器数量</returns>
|
||||||
|
public int GetListenerCount()
|
||||||
|
{
|
||||||
|
return _mOnEvent?.GetInvocationList().Length ?? 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -120,4 +129,13 @@ public class Event<T, Tk> : IEvent
|
|||||||
{
|
{
|
||||||
_mOnEvent?.Invoke(t, k);
|
_mOnEvent?.Invoke(t, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前已注册的监听器数量
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>监听器数量</returns>
|
||||||
|
public int GetListenerCount()
|
||||||
|
{
|
||||||
|
return _mOnEvent?.GetInvocationList().Length ?? 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
390
GFramework.Core/events/EnhancedEventBus.cs
Normal file
390
GFramework.Core/events/EnhancedEventBus.cs
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增强的事件总线,支持统计和过滤器
|
||||||
|
/// 线程安全:使用 ConcurrentDictionary 存储事件
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EnhancedEventBus : IEventBus
|
||||||
|
{
|
||||||
|
private readonly EasyEvents _mEvents = new();
|
||||||
|
private readonly ConcurrentDictionary<Type, object> _mFilterableEvents = new();
|
||||||
|
private readonly EasyEvents _mPriorityEvents = new();
|
||||||
|
private readonly ConcurrentDictionary<Type, object> _mWeakEvents = new();
|
||||||
|
private readonly EventStatistics? _statistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableStatistics">是否启用统计功能</param>
|
||||||
|
public EnhancedEventBus(bool enableStatistics = false)
|
||||||
|
{
|
||||||
|
_statistics = enableStatistics ? new EventStatistics() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取事件统计信息(如果启用)
|
||||||
|
/// </summary>
|
||||||
|
public IEventStatistics? Statistics => _statistics;
|
||||||
|
|
||||||
|
#region IEventBus Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送指定类型的事件实例(自动创建默认实例)
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
public void Send<T>() where T : new()
|
||||||
|
{
|
||||||
|
_statistics?.RecordPublish(typeof(T).Name);
|
||||||
|
|
||||||
|
_mEvents
|
||||||
|
.GetOrAddEvent<Event<T>>()
|
||||||
|
.Trigger(new T());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送指定类型的事件实例
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="e">事件实例</param>
|
||||||
|
public void Send<T>(T e)
|
||||||
|
{
|
||||||
|
_statistics?.RecordPublish(typeof(T).Name);
|
||||||
|
|
||||||
|
_mEvents
|
||||||
|
.GetOrAddEvent<Event<T>>()
|
||||||
|
.Trigger(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送具有指定传播方式的优先级事件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="e">事件实例</param>
|
||||||
|
/// <param name="propagation">事件传播方式</param>
|
||||||
|
public void Send<T>(T e, EventPropagation propagation)
|
||||||
|
{
|
||||||
|
_statistics?.RecordPublish(typeof(T).Name);
|
||||||
|
|
||||||
|
_mPriorityEvents
|
||||||
|
.GetOrAddEvent<PriorityEvent<T>>()
|
||||||
|
.Trigger(e, propagation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <returns>反注册接口,用于取消订阅</returns>
|
||||||
|
public IUnRegister Register<T>(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
if (_statistics != null)
|
||||||
|
{
|
||||||
|
// 包装回调以添加统计
|
||||||
|
Action<T> wrappedHandler = data =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
onEvent(data);
|
||||||
|
_statistics.RecordHandle();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_statistics.RecordFailure();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var unregister = _mEvents.GetOrAddEvent<Event<T>>().Register(wrappedHandler);
|
||||||
|
UpdateEventListenerCount<T>();
|
||||||
|
return new DefaultUnRegister(() =>
|
||||||
|
{
|
||||||
|
unregister.UnRegister();
|
||||||
|
UpdateEventListenerCount<T>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mEvents.GetOrAddEvent<Event<T>>().Register(onEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册具有优先级的监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <param name="priority">优先级,数值越大优先级越高</param>
|
||||||
|
/// <returns>反注册接口,用于取消订阅</returns>
|
||||||
|
public IUnRegister Register<T>(Action<T> onEvent, int priority)
|
||||||
|
{
|
||||||
|
if (_statistics != null)
|
||||||
|
{
|
||||||
|
// 包装回调以添加统计
|
||||||
|
Action<T> wrappedHandler = data =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
onEvent(data);
|
||||||
|
_statistics.RecordHandle();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_statistics.RecordFailure();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var unregister = _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().Register(wrappedHandler, priority);
|
||||||
|
UpdatePriorityEventListenerCount<T>();
|
||||||
|
return new DefaultUnRegister(() =>
|
||||||
|
{
|
||||||
|
unregister.UnRegister();
|
||||||
|
UpdatePriorityEventListenerCount<T>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().Register(onEvent, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注销事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
public void UnRegister<T>(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
_mEvents.GetEvent<Event<T>>().UnRegister(onEvent);
|
||||||
|
UpdateEventListenerCount<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册带有上下文信息的优先级事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调,接收事件上下文</param>
|
||||||
|
/// <returns>反注册接口,用于取消订阅</returns>
|
||||||
|
public IUnRegister RegisterWithContext<T>(Action<EventContext<T>> onEvent)
|
||||||
|
{
|
||||||
|
if (_statistics != null)
|
||||||
|
{
|
||||||
|
// 包装回调以添加统计
|
||||||
|
Action<EventContext<T>> wrappedHandler = context =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
onEvent(context);
|
||||||
|
_statistics.RecordHandle();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_statistics.RecordFailure();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var unregister = _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().RegisterWithContext(wrappedHandler);
|
||||||
|
UpdatePriorityEventListenerCount<T>();
|
||||||
|
return new DefaultUnRegister(() =>
|
||||||
|
{
|
||||||
|
unregister.UnRegister();
|
||||||
|
UpdatePriorityEventListenerCount<T>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().RegisterWithContext(onEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册带有上下文信息和优先级的监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调,接收事件上下文</param>
|
||||||
|
/// <param name="priority">优先级,数值越大优先级越高</param>
|
||||||
|
/// <returns>反注册接口,用于取消订阅</returns>
|
||||||
|
public IUnRegister RegisterWithContext<T>(Action<EventContext<T>> onEvent, int priority)
|
||||||
|
{
|
||||||
|
if (_statistics != null)
|
||||||
|
{
|
||||||
|
// 包装回调以添加统计
|
||||||
|
Action<EventContext<T>> wrappedHandler = context =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
onEvent(context);
|
||||||
|
_statistics.RecordHandle();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_statistics.RecordFailure();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var unregister = _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>()
|
||||||
|
.RegisterWithContext(wrappedHandler, priority);
|
||||||
|
UpdatePriorityEventListenerCount<T>();
|
||||||
|
return new DefaultUnRegister(() =>
|
||||||
|
{
|
||||||
|
unregister.UnRegister();
|
||||||
|
UpdatePriorityEventListenerCount<T>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().RegisterWithContext(onEvent, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Filterable Events
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送支持过滤的事件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="e">事件实例</param>
|
||||||
|
public void SendFilterable<T>(T e)
|
||||||
|
{
|
||||||
|
var evt = (FilterableEvent<T>)_mFilterableEvents.GetOrAdd(
|
||||||
|
typeof(T),
|
||||||
|
_ => new FilterableEvent<T>(_statistics));
|
||||||
|
evt.Trigger(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册支持过滤的事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <returns>反注册接口</returns>
|
||||||
|
public IUnRegister RegisterFilterable<T>(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
var evt = (FilterableEvent<T>)_mFilterableEvents.GetOrAdd(
|
||||||
|
typeof(T),
|
||||||
|
_ => new FilterableEvent<T>(_statistics));
|
||||||
|
return evt.Register(onEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为指定事件类型添加过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="filter">过滤器</param>
|
||||||
|
public void AddFilter<T>(IEventFilter<T> filter)
|
||||||
|
{
|
||||||
|
var evt = (FilterableEvent<T>)_mFilterableEvents.GetOrAdd(
|
||||||
|
typeof(T),
|
||||||
|
_ => new FilterableEvent<T>(_statistics));
|
||||||
|
evt.AddFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除指定事件类型的过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="filter">过滤器</param>
|
||||||
|
public void RemoveFilter<T>(IEventFilter<T> filter)
|
||||||
|
{
|
||||||
|
if (_mFilterableEvents.TryGetValue(typeof(T), out var obj))
|
||||||
|
{
|
||||||
|
var evt = (FilterableEvent<T>)obj;
|
||||||
|
evt.RemoveFilter(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除指定事件类型的所有过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
public void ClearFilters<T>()
|
||||||
|
{
|
||||||
|
if (_mFilterableEvents.TryGetValue(typeof(T), out var obj))
|
||||||
|
{
|
||||||
|
var evt = (FilterableEvent<T>)obj;
|
||||||
|
evt.ClearFilters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Weak Events
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送弱引用事件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="e">事件实例</param>
|
||||||
|
public void SendWeak<T>(T e)
|
||||||
|
{
|
||||||
|
var evt = (WeakEvent<T>)_mWeakEvents.GetOrAdd(
|
||||||
|
typeof(T),
|
||||||
|
_ => new WeakEvent<T>(_statistics));
|
||||||
|
evt.Trigger(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册弱引用事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <returns>反注册接口</returns>
|
||||||
|
public IUnRegister RegisterWeak<T>(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
var evt = (WeakEvent<T>)_mWeakEvents.GetOrAdd(
|
||||||
|
typeof(T),
|
||||||
|
_ => new WeakEvent<T>(_statistics));
|
||||||
|
return evt.Register(onEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理指定事件类型的已回收弱引用
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
public void CleanupWeak<T>()
|
||||||
|
{
|
||||||
|
if (_mWeakEvents.TryGetValue(typeof(T), out var obj))
|
||||||
|
{
|
||||||
|
var evt = (WeakEvent<T>)obj;
|
||||||
|
evt.Cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新普通事件的监听器数量统计
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateEventListenerCount<T>()
|
||||||
|
{
|
||||||
|
if (_statistics == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var evt = _mEvents.GetEvent<Event<T>>();
|
||||||
|
if (evt != null)
|
||||||
|
{
|
||||||
|
var count = evt.GetListenerCount();
|
||||||
|
_statistics.UpdateListenerCount(typeof(T).Name, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新优先级事件的监听器数量统计
|
||||||
|
/// </summary>
|
||||||
|
private void UpdatePriorityEventListenerCount<T>()
|
||||||
|
{
|
||||||
|
if (_statistics == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var evt = _mPriorityEvents.GetEvent<PriorityEvent<T>>();
|
||||||
|
if (evt != null)
|
||||||
|
{
|
||||||
|
var count = evt.GetListenerCount();
|
||||||
|
_statistics.UpdateListenerCount(typeof(T).Name, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
165
GFramework.Core/events/EventStatistics.cs
Normal file
165
GFramework.Core/events/EventStatistics.cs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
using System.Text;
|
||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件统计信息实现类
|
||||||
|
/// 线程安全:使用 Interlocked 操作确保计数器的原子性
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EventStatistics : IEventStatistics
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, int> _listenerCountByType = new();
|
||||||
|
private readonly object _lock = new();
|
||||||
|
private readonly Dictionary<string, long> _publishCountByType = new();
|
||||||
|
private long _totalFailed;
|
||||||
|
private long _totalHandled;
|
||||||
|
private long _totalPublished;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long TotalPublished => Interlocked.Read(ref _totalPublished);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long TotalHandled => Interlocked.Read(ref _totalHandled);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long TotalFailed => Interlocked.Read(ref _totalFailed);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int ActiveEventTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _publishCountByType.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int ActiveListeners
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _listenerCountByType.Values.Sum();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public long GetPublishCount(string eventType)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _publishCountByType.TryGetValue(eventType, out var count) ? count : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int GetListenerCount(string eventType)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _listenerCountByType.TryGetValue(eventType, out var count) ? count : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _totalPublished, 0);
|
||||||
|
Interlocked.Exchange(ref _totalHandled, 0);
|
||||||
|
Interlocked.Exchange(ref _totalFailed, 0);
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_publishCountByType.Clear();
|
||||||
|
_listenerCountByType.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录事件发布
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType">事件类型名称</param>
|
||||||
|
public void RecordPublish(string eventType)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _totalPublished);
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_publishCountByType.TryGetValue(eventType, out var count);
|
||||||
|
_publishCountByType[eventType] = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录事件处理
|
||||||
|
/// </summary>
|
||||||
|
public void RecordHandle()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _totalHandled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录事件处理失败
|
||||||
|
/// </summary>
|
||||||
|
public void RecordFailure()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _totalFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新事件类型的监听器数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType">事件类型名称</param>
|
||||||
|
/// <param name="count">监听器数量</param>
|
||||||
|
public void UpdateListenerCount(string eventType, int count)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
_listenerCountByType[eventType] = count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_listenerCountByType.Remove(eventType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
155
GFramework.Core/events/FilterableEvent.cs
Normal file
155
GFramework.Core/events/FilterableEvent.cs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 支持过滤器和统计的泛型事件类
|
||||||
|
/// 线程安全:使用锁保护监听器列表和过滤器列表的修改
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件数据类型</typeparam>
|
||||||
|
public sealed class FilterableEvent<T>
|
||||||
|
{
|
||||||
|
private readonly List<IEventFilter<T>> _filters = new();
|
||||||
|
private readonly object _lock = new();
|
||||||
|
private readonly EventStatistics? _statistics;
|
||||||
|
private Action<T>? _onEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="statistics">事件统计对象(可选)</param>
|
||||||
|
public FilterableEvent(EventStatistics? statistics = null)
|
||||||
|
{
|
||||||
|
_statistics = statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <returns>反注册接口</returns>
|
||||||
|
public IUnRegister Register(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_onEvent += onEvent;
|
||||||
|
UpdateListenerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultUnRegister(() => UnRegister(onEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注销事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
public void UnRegister(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_onEvent -= onEvent;
|
||||||
|
UpdateListenerCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">事件数据</param>
|
||||||
|
public void Trigger(T data)
|
||||||
|
{
|
||||||
|
// 记录发布统计
|
||||||
|
_statistics?.RecordPublish(typeof(T).Name);
|
||||||
|
|
||||||
|
// 在单个锁中快照过滤器和监听器
|
||||||
|
Action<T>? handlers;
|
||||||
|
IEventFilter<T>[] filtersSnapshot;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
filtersSnapshot = _filters.Count > 0 ? _filters.ToArray() : Array.Empty<IEventFilter<T>>();
|
||||||
|
handlers = _onEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在锁外执行过滤逻辑
|
||||||
|
// 事件被过滤,不触发监听器
|
||||||
|
if (filtersSnapshot.Any(filter => filter.ShouldFilter(data)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (handlers == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 在锁外调用监听器,避免死锁
|
||||||
|
foreach (var handler in handlers.GetInvocationList().Cast<Action<T>>())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handler(data);
|
||||||
|
_statistics?.RecordHandle();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_statistics?.RecordFailure();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加事件过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filter">过滤器</param>
|
||||||
|
public void AddFilter(IEventFilter<T> filter)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_filters.Add(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除事件过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filter">过滤器</param>
|
||||||
|
public void RemoveFilter(IEventFilter<T> filter)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_filters.Remove(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除所有过滤器
|
||||||
|
/// </summary>
|
||||||
|
public void ClearFilters()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_filters.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前监听器数量
|
||||||
|
/// </summary>
|
||||||
|
public int GetListenerCount()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _onEvent?.GetInvocationList().Length ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新监听器数量统计
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateListenerCount()
|
||||||
|
{
|
||||||
|
if (_statistics == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var count = _onEvent?.GetInvocationList().Length ?? 0;
|
||||||
|
_statistics.UpdateListenerCount(typeof(T).Name, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -254,6 +254,15 @@ public class PriorityEvent<T> : IEvent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前已注册的监听器总数量(包括普通监听器和上下文监听器)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>监听器总数量</returns>
|
||||||
|
public int GetListenerCount()
|
||||||
|
{
|
||||||
|
return _handlers.Count + _contextHandlers.Count;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 事件处理器包装类,包含处理器和优先级
|
/// 事件处理器包装类,包含处理器和优先级
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
108
GFramework.Core/events/StatisticsEventDecorator.cs
Normal file
108
GFramework.Core/events/StatisticsEventDecorator.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 带统计功能的事件装饰器
|
||||||
|
/// 使用装饰器模式为任何 IEvent 实现添加统计功能
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件数据类型</typeparam>
|
||||||
|
internal sealed class StatisticsEventDecorator<T>
|
||||||
|
{
|
||||||
|
private readonly string _eventTypeName;
|
||||||
|
private readonly IEvent _innerEvent;
|
||||||
|
private readonly EventStatistics _statistics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="innerEvent">被装饰的事件对象</param>
|
||||||
|
/// <param name="statistics">统计对象</param>
|
||||||
|
public StatisticsEventDecorator(IEvent innerEvent, EventStatistics statistics)
|
||||||
|
{
|
||||||
|
_innerEvent = innerEvent ?? throw new ArgumentNullException(nameof(innerEvent));
|
||||||
|
_statistics = statistics ?? throw new ArgumentNullException(nameof(statistics));
|
||||||
|
_eventTypeName = typeof(T).Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册事件监听器(带统计)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <returns>反注册接口</returns>
|
||||||
|
public IUnRegister Register(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
// 包装回调以添加统计
|
||||||
|
Action<T> wrappedHandler = data =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
onEvent(data);
|
||||||
|
_statistics.RecordHandle();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_statistics.RecordFailure();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var unregister = _innerEvent.Register(() => { }); // 占位,实际不使用
|
||||||
|
|
||||||
|
// 直接注册到内部事件
|
||||||
|
if (_innerEvent is Event<T> typedEvent)
|
||||||
|
{
|
||||||
|
unregister = typedEvent.Register(wrappedHandler);
|
||||||
|
}
|
||||||
|
else if (_innerEvent is PriorityEvent<T> priorityEvent)
|
||||||
|
{
|
||||||
|
unregister = priorityEvent.Register(wrappedHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新监听器统计
|
||||||
|
UpdateListenerCount();
|
||||||
|
|
||||||
|
return new DefaultUnRegister(() =>
|
||||||
|
{
|
||||||
|
unregister.UnRegister();
|
||||||
|
UpdateListenerCount();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发事件(带统计)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">事件数据</param>
|
||||||
|
public void Trigger(T data)
|
||||||
|
{
|
||||||
|
_statistics.RecordPublish(_eventTypeName);
|
||||||
|
|
||||||
|
if (_innerEvent is Event<T> typedEvent)
|
||||||
|
{
|
||||||
|
typedEvent.Trigger(data);
|
||||||
|
}
|
||||||
|
else if (_innerEvent is PriorityEvent<T> priorityEvent)
|
||||||
|
{
|
||||||
|
priorityEvent.Trigger(data, EventPropagation.All);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新监听器数量统计
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateListenerCount()
|
||||||
|
{
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
if (_innerEvent is Event<T> typedEvent)
|
||||||
|
{
|
||||||
|
count = typedEvent.GetListenerCount();
|
||||||
|
}
|
||||||
|
else if (_innerEvent is PriorityEvent<T> priorityEvent)
|
||||||
|
{
|
||||||
|
count = priorityEvent.GetListenerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
_statistics.UpdateListenerCount(_eventTypeName, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
154
GFramework.Core/events/WeakEvent.cs
Normal file
154
GFramework.Core/events/WeakEvent.cs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 支持弱引用订阅的泛型事件类
|
||||||
|
/// 使用弱引用存储监听器,避免事件订阅导致的内存泄漏
|
||||||
|
/// 线程安全:使用锁保护监听器列表的修改
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件数据类型</typeparam>
|
||||||
|
public sealed class WeakEvent<T>
|
||||||
|
{
|
||||||
|
private readonly object _lock = new();
|
||||||
|
private readonly EventStatistics? _statistics;
|
||||||
|
private readonly List<WeakReference<Action<T>>> _weakHandlers = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="statistics">事件统计对象(可选)</param>
|
||||||
|
public WeakEvent(EventStatistics? statistics = null)
|
||||||
|
{
|
||||||
|
_statistics = statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册事件监听器(弱引用)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
/// <returns>反注册接口</returns>
|
||||||
|
public IUnRegister Register(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_weakHandlers.Add(new WeakReference<Action<T>>(onEvent));
|
||||||
|
UpdateListenerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultUnRegister(() => UnRegister(onEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注销事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onEvent">事件处理回调</param>
|
||||||
|
public void UnRegister(Action<T> onEvent)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_weakHandlers.RemoveAll(wr =>
|
||||||
|
{
|
||||||
|
if (!wr.TryGetTarget(out var target))
|
||||||
|
return true; // 目标已被回收,移除
|
||||||
|
return ReferenceEquals(target, onEvent);
|
||||||
|
});
|
||||||
|
UpdateListenerCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">事件数据</param>
|
||||||
|
public void Trigger(T data)
|
||||||
|
{
|
||||||
|
// 记录发布统计
|
||||||
|
_statistics?.RecordPublish(typeof(T).Name);
|
||||||
|
|
||||||
|
// 收集存活的监听器
|
||||||
|
var aliveHandlers = new List<Action<T>>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理已回收的弱引用
|
||||||
|
/// </summary>
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
var beforeCount = _weakHandlers.Count;
|
||||||
|
_weakHandlers.RemoveAll(wr => !wr.TryGetTarget(out _));
|
||||||
|
if (_weakHandlers.Count != beforeCount)
|
||||||
|
UpdateListenerCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前存活的监听器数量
|
||||||
|
/// </summary>
|
||||||
|
public int GetListenerCount()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _weakHandlers.Count(wr => wr.TryGetTarget(out _));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新监听器数量统计
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateListenerCount()
|
||||||
|
{
|
||||||
|
if (_statistics == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var count = _weakHandlers.Count(wr => wr.TryGetTarget(out _));
|
||||||
|
_statistics.UpdateListenerCount(typeof(T).Name, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
GFramework.Core/events/filters/PredicateEventFilter.cs
Normal file
28
GFramework.Core/events/filters/PredicateEventFilter.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events.filters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 基于谓词的事件过滤器
|
||||||
|
/// 允许使用自定义条件函数来过滤事件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
public sealed class PredicateEventFilter<T> : IEventFilter<T>
|
||||||
|
{
|
||||||
|
private readonly Func<T, bool> _predicate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="predicate">过滤条件函数,返回 true 表示过滤(阻止),返回 false 表示允许</param>
|
||||||
|
public PredicateEventFilter(Func<T, bool> predicate)
|
||||||
|
{
|
||||||
|
_predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool ShouldFilter(T eventData)
|
||||||
|
{
|
||||||
|
return _predicate(eventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
GFramework.Core/events/filters/SamplingEventFilter.cs
Normal file
40
GFramework.Core/events/filters/SamplingEventFilter.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
namespace GFramework.Core.events.filters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 采样事件过滤器
|
||||||
|
/// 按照指定的采样率过滤事件,用于限制高频事件的处理
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
public sealed class SamplingEventFilter<T> : IEventFilter<T>
|
||||||
|
{
|
||||||
|
private readonly double _samplingRate;
|
||||||
|
private long _counter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="samplingRate">采样率,范围 0.0 到 1.0。例如 0.1 表示只允许 10% 的事件通过</param>
|
||||||
|
public SamplingEventFilter(double samplingRate)
|
||||||
|
{
|
||||||
|
if (samplingRate < 0.0 || samplingRate > 1.0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(samplingRate), "采样率必须在 0.0 到 1.0 之间");
|
||||||
|
|
||||||
|
_samplingRate = samplingRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user