mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 04:59:01 +08:00
feat(events): 添加事件上下文支持和线程安全改进
- 在EventBus中添加RegisterWithContext方法支持事件上下文 - 实现EventContext<T>类包装事件数据并提供处理控制 - 在PriorityEvent中添加上下文事件处理器和相关管理逻辑 - 改进事件触发机制支持普通和上下文处理器混合使用 - 优化MicrosoftDiContainer的线程安全性和并发访问 - 修复SpanExtensions中TryParseValue的返回值类型问题 - 添加全面的单元测试覆盖新功能和边界情况
This commit is contained in:
parent
e2dca4f5a6
commit
eec50ab45b
26
GFramework.Core.Abstractions/events/EventContext.cs
Normal file
26
GFramework.Core.Abstractions/events/EventContext.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
namespace GFramework.Core.Abstractions.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件上下文,包装事件数据并提供控制方法
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件数据类型</typeparam>
|
||||||
|
public class EventContext<T>(T data)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 事件数据
|
||||||
|
/// </summary>
|
||||||
|
public T Data { get; } = data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件是否已被处理
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHandled { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记事件为已处理,停止后续传播(仅对 UntilHandled 模式有效)
|
||||||
|
/// </summary>
|
||||||
|
public void MarkAsHandled()
|
||||||
|
{
|
||||||
|
IsHandled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -234,4 +234,127 @@ public class EventBusPriorityTests
|
|||||||
// Assert
|
// Assert
|
||||||
Assert.That(executionOrder, Is.EqualTo(new[] { "first", "second", "third" }));
|
Assert.That(executionOrder, Is.EqualTo(new[] { "first", "second", "third" }));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[Test]
|
||||||
|
public void UntilHandled_Should_Stop_After_MarkAsHandled()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EventBus();
|
||||||
|
var executionOrder = new List<int>();
|
||||||
|
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx =>
|
||||||
|
{
|
||||||
|
executionOrder.Add(1);
|
||||||
|
ctx.MarkAsHandled();
|
||||||
|
}, priority: 10);
|
||||||
|
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx =>
|
||||||
|
{
|
||||||
|
executionOrder.Add(2); // 不应该执行
|
||||||
|
}, priority: 5);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
eventBus.Send(new TestEvent(), EventPropagation.UntilHandled);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder, Is.EqualTo(new[] { 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UntilHandled_Should_Execute_All_If_Not_Handled()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EventBus();
|
||||||
|
var executionOrder = new List<int>();
|
||||||
|
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx => executionOrder.Add(1), priority: 10);
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx => executionOrder.Add(2), priority: 5);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
eventBus.Send(new TestEvent(), EventPropagation.UntilHandled);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder, Is.EqualTo(new[] { 1, 2 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RegisterWithContext_Should_Receive_Event_Data()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EventBus();
|
||||||
|
string? receivedMessage = null;
|
||||||
|
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx => { receivedMessage = ctx.Data.Message; });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
eventBus.Send(new TestEvent { Message = "Hello" }, EventPropagation.All);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(receivedMessage, Is.EqualTo("Hello"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UntilHandled_Should_Respect_Priority_Order()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EventBus();
|
||||||
|
var executionOrder = new List<int>();
|
||||||
|
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx => executionOrder.Add(1), priority: 1);
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx =>
|
||||||
|
{
|
||||||
|
executionOrder.Add(3);
|
||||||
|
ctx.MarkAsHandled();
|
||||||
|
}, priority: 3);
|
||||||
|
eventBus.RegisterWithContext<TestEvent>(ctx => executionOrder.Add(2), priority: 2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
eventBus.Send(new TestEvent(), EventPropagation.UntilHandled);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder, Is.EqualTo(new[] { 3 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Handler_Can_Unregister_Itself_Without_Exception()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EventBus();
|
||||||
|
var executionCount = 0;
|
||||||
|
IUnRegister? unregister = null;
|
||||||
|
|
||||||
|
unregister = eventBus.Register<TestEvent>(_ =>
|
||||||
|
{
|
||||||
|
executionCount++;
|
||||||
|
unregister?.UnRegister();
|
||||||
|
}, priority: 1);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.DoesNotThrow(() => eventBus.Send(new TestEvent(), EventPropagation.All));
|
||||||
|
Assert.That(executionCount, Is.EqualTo(1));
|
||||||
|
|
||||||
|
// 第二次触发不应执行
|
||||||
|
eventBus.Send(new TestEvent(), EventPropagation.All);
|
||||||
|
Assert.That(executionCount, Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Concurrent_Trigger_And_Register_Should_Be_Thread_Safe()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var eventBus = new EventBus();
|
||||||
|
var counter = 0;
|
||||||
|
eventBus.Register<TestEvent>(_ => Interlocked.Increment(ref counter), priority: 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = Enumerable.Range(0, 100).Select(_ => Task.Run(() =>
|
||||||
|
{
|
||||||
|
eventBus.Send(new TestEvent(), EventPropagation.All);
|
||||||
|
eventBus.Register<TestEvent>(_ => { }, priority: 1);
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotThrow(() => Task.WaitAll(tasks));
|
||||||
|
Assert.That(counter, Is.GreaterThan(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
165
GFramework.Core.Tests/events/PriorityEventTests.cs
Normal file
165
GFramework.Core.Tests/events/PriorityEventTests.cs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
using GFramework.Core.Abstractions.events;
|
||||||
|
using GFramework.Core.events;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试 PriorityEvent 的线程安全性和边界情况
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class PriorityEventTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Trigger_Should_Not_Throw_When_Handler_Unregisters_Itself()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
IUnRegister? unregister = null;
|
||||||
|
|
||||||
|
unregister = evt.Register(x => { unregister?.UnRegister(); });
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.DoesNotThrow(() => evt.Trigger(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Trigger_Should_Be_Thread_Safe()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
var counter = 0;
|
||||||
|
evt.Register(x => Interlocked.Increment(ref counter));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = Enumerable.Range(0, 100).Select(_ => Task.Run(() =>
|
||||||
|
{
|
||||||
|
evt.Trigger(1);
|
||||||
|
evt.Register(x => { });
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotThrow(() => Task.WaitAll(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Multiple_Handlers_Unregistering_During_Trigger_Should_Not_Throw()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
var unregisters = new List<IUnRegister>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
var unreg = evt.Register(x =>
|
||||||
|
{
|
||||||
|
if (index % 2 == 0)
|
||||||
|
{
|
||||||
|
unregisters[index].UnRegister();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unregisters.Add(unreg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.DoesNotThrow(() => evt.Trigger(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Context_Handler_Should_Receive_Correct_Data()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<string>();
|
||||||
|
string? receivedData = null;
|
||||||
|
|
||||||
|
evt.RegisterWithContext(ctx => { receivedData = ctx.Data; });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
evt.Trigger("test data", EventPropagation.All);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(receivedData, Is.EqualTo("test data"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Context_Handler_MarkAsHandled_Should_Stop_UntilHandled_Propagation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
var executionOrder = new List<int>();
|
||||||
|
|
||||||
|
evt.RegisterWithContext(ctx =>
|
||||||
|
{
|
||||||
|
executionOrder.Add(1);
|
||||||
|
ctx.MarkAsHandled();
|
||||||
|
}, priority: 10);
|
||||||
|
|
||||||
|
evt.RegisterWithContext(ctx => { executionOrder.Add(2); }, priority: 5);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
evt.Trigger(42, EventPropagation.UntilHandled);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder, Is.EqualTo(new[] { 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Mixed_Normal_And_Context_Handlers_Should_Work_Together()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
var executionOrder = new List<string>();
|
||||||
|
|
||||||
|
evt.Register(x => executionOrder.Add("normal"), priority: 5);
|
||||||
|
evt.RegisterWithContext(ctx => executionOrder.Add("context"), priority: 10);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
evt.Trigger(1, EventPropagation.All);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder, Is.EqualTo(new[] { "context", "normal" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UntilHandled_With_Mixed_Handlers_Should_Respect_Priority()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
var executionOrder = new List<string>();
|
||||||
|
|
||||||
|
evt.Register(x => executionOrder.Add("normal-low"), priority: 1);
|
||||||
|
evt.RegisterWithContext(ctx =>
|
||||||
|
{
|
||||||
|
executionOrder.Add("context-high");
|
||||||
|
ctx.MarkAsHandled();
|
||||||
|
}, priority: 10);
|
||||||
|
evt.Register(x => executionOrder.Add("normal-mid"), priority: 5);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
evt.Trigger(1, EventPropagation.UntilHandled);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder, Is.EqualTo(new[] { "context-high" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Highest_Propagation_Should_Execute_All_Highest_Priority_Handlers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var evt = new PriorityEvent<int>();
|
||||||
|
var executionOrder = new List<string>();
|
||||||
|
|
||||||
|
evt.Register(x => executionOrder.Add("normal-high"), priority: 10);
|
||||||
|
evt.RegisterWithContext(ctx => executionOrder.Add("context-high"), priority: 10);
|
||||||
|
evt.Register(x => executionOrder.Add("normal-low"), priority: 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
evt.Trigger(1, EventPropagation.Highest);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(executionOrder.Count, Is.EqualTo(2));
|
||||||
|
Assert.That(executionOrder, Does.Contain("normal-high"));
|
||||||
|
Assert.That(executionOrder, Does.Contain("context-high"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,7 +30,6 @@ public class MicrosoftDiContainerTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MicrosoftDiContainer _container = null!;
|
private MicrosoftDiContainer _container = null!;
|
||||||
private readonly Dictionary<Type, object> _mockContextServices = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 测试注册单例实例的功能
|
/// 测试注册单例实例的功能
|
||||||
@ -249,12 +248,18 @@ public class MicrosoftDiContainerTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void Contains_WithExistingInstance_Should_ReturnTrue()
|
public void Contains_WithExistingInstance_Should_ReturnTrue()
|
||||||
{
|
{
|
||||||
|
// 使用 RegisterSingleton 方法来避免与其他测试方法重复
|
||||||
var instance = new TestService();
|
var instance = new TestService();
|
||||||
_container.Register(instance);
|
|
||||||
|
|
||||||
|
_container.RegisterSingleton(instance);
|
||||||
|
|
||||||
|
// 验证容器包含该实例
|
||||||
Assert.That(_container.Contains<TestService>(), Is.True);
|
Assert.That(_container.Contains<TestService>(), Is.True);
|
||||||
|
// 验证实例确实是单例
|
||||||
|
Assert.That(_container.Get<TestService>(), Is.SameAs(instance));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 测试当不存在实例时检查包含关系应返回 false 的功能
|
/// 测试当不存在实例时检查包含关系应返回 false 的功能
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -327,6 +332,107 @@ public class MicrosoftDiContainerTests
|
|||||||
|
|
||||||
Assert.That(_container.Contains<TestSystem>(), Is.True);
|
Assert.That(_container.Contains<TestSystem>(), Is.True);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试在容器未冻结时调用 CreateScope 应抛出异常
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CreateScope_Should_Throw_When_Not_Frozen()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var container = new MicrosoftDiContainer();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() => container.CreateScope());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试 CreateScope 在多线程环境下的线程安全性
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CreateScope_Should_Be_Thread_Safe()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var container = new MicrosoftDiContainer();
|
||||||
|
container.RegisterSingleton<IService>(new TestService());
|
||||||
|
container.Freeze();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = Enumerable.Range(0, 100).Select(_ => Task.Run(() =>
|
||||||
|
{
|
||||||
|
using var scope = container.CreateScope();
|
||||||
|
Assert.That(scope, Is.Not.Null);
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotThrow(() => Task.WaitAll(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试 Get 方法在多线程环境下的线程安全性
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Get_Should_Be_Thread_Safe()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var container = new MicrosoftDiContainer();
|
||||||
|
container.RegisterSingleton<IService>(new TestService());
|
||||||
|
container.Freeze();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = Enumerable.Range(0, 100).Select(_ => Task.Run(() =>
|
||||||
|
{
|
||||||
|
var service = container.Get<IService>();
|
||||||
|
Assert.That(service, Is.Not.Null);
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotThrow(() => Task.WaitAll(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试 GetAll 方法在多线程环境下的线程安全性
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void GetAll_Should_Be_Thread_Safe()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var container = new MicrosoftDiContainer();
|
||||||
|
container.RegisterSingleton<IService>(new TestService());
|
||||||
|
container.Freeze();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = Enumerable.Range(0, 100).Select(_ => Task.Run(() =>
|
||||||
|
{
|
||||||
|
var services = container.GetAll<IService>();
|
||||||
|
Assert.That(services, Is.Not.Null);
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotThrow(() => Task.WaitAll(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试 Contains 方法在多线程环境下的线程安全性
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Contains_Should_Be_Thread_Safe()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var container = new MicrosoftDiContainer();
|
||||||
|
container.RegisterSingleton<IService>(new TestService());
|
||||||
|
container.Freeze();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = Enumerable.Range(0, 100).Select(_ => Task.Run(() =>
|
||||||
|
{
|
||||||
|
var contains = container.Contains<IService>();
|
||||||
|
Assert.That(contains, Is.True);
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotThrow(() => Task.WaitAll(tasks));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -78,4 +78,27 @@ public class EventBus : IEventBus
|
|||||||
{
|
{
|
||||||
_mEvents.GetEvent<Event<T>>().UnRegister(onEvent);
|
_mEvents.GetEvent<Event<T>>().UnRegister(onEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册上下文事件监听器,默认优先级为 0
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调函数,接收 EventContext 参数</param>
|
||||||
|
/// <returns>反注册接口,用于注销事件监听</returns>
|
||||||
|
public IUnRegister RegisterWithContext<T>(Action<EventContext<T>> onEvent)
|
||||||
|
{
|
||||||
|
return _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().RegisterWithContext(onEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册上下文事件监听器,并指定优先级
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">事件类型</typeparam>
|
||||||
|
/// <param name="onEvent">事件处理回调函数,接收 EventContext 参数</param>
|
||||||
|
/// <param name="priority">优先级,数值越大优先级越高</param>
|
||||||
|
/// <returns>反注册接口,用于注销事件监听</returns>
|
||||||
|
public IUnRegister RegisterWithContext<T>(Action<EventContext<T>> onEvent, int priority)
|
||||||
|
{
|
||||||
|
return _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().RegisterWithContext(onEvent, priority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -9,19 +9,9 @@ namespace GFramework.Core.events;
|
|||||||
public class PriorityEvent<T> : IEvent
|
public class PriorityEvent<T> : IEvent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 事件处理器包装类,包含处理器和优先级
|
/// 存储已注册的上下文事件处理器列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class EventHandler
|
private readonly List<ContextEventHandler> _contextHandlers = new();
|
||||||
{
|
|
||||||
public Action<T> Handler { get; }
|
|
||||||
public int Priority { get; }
|
|
||||||
|
|
||||||
public EventHandler(Action<T> handler, int priority)
|
|
||||||
{
|
|
||||||
Handler = handler;
|
|
||||||
Priority = priority;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存储已注册的事件处理器列表
|
/// 存储已注册的事件处理器列表
|
||||||
@ -80,12 +70,29 @@ public class PriorityEvent<T> : IEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 触发所有已注册的事件处理程序(默认传播模式:All)
|
/// 注册一个上下文事件监听器,并指定优先级
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="t">传递给事件处理程序的参数</param>
|
/// <param name="onEvent">要注册的事件处理方法,接收 EventContext 参数</param>
|
||||||
public void Trigger(T t)
|
/// <param name="priority">优先级,数值越大优先级越高</param>
|
||||||
|
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器</returns>
|
||||||
|
public IUnRegister RegisterWithContext(Action<EventContext<T>> onEvent, int priority = 0)
|
||||||
{
|
{
|
||||||
Trigger(t, EventPropagation.All);
|
var handler = new ContextEventHandler(onEvent, priority);
|
||||||
|
_contextHandlers.Add(handler);
|
||||||
|
|
||||||
|
// 按优先级降序排序(高优先级在前)
|
||||||
|
_contextHandlers.Sort((a, b) => b.Priority.CompareTo(a.Priority));
|
||||||
|
|
||||||
|
return new DefaultUnRegister(() => UnRegisterContext(onEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取消指定的上下文事件监听器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onEvent">需要被注销的事件处理方法</param>
|
||||||
|
public void UnRegisterContext(Action<EventContext<T>> onEvent)
|
||||||
|
{
|
||||||
|
_contextHandlers.RemoveAll(h => h.Handler == onEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -93,49 +100,116 @@ public class PriorityEvent<T> : IEvent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="t">传递给事件处理程序的参数</param>
|
/// <param name="t">传递给事件处理程序的参数</param>
|
||||||
/// <param name="propagation">事件传播模式</param>
|
/// <param name="propagation">事件传播模式</param>
|
||||||
public void Trigger(T t, EventPropagation propagation)
|
public void Trigger(T t, EventPropagation propagation = EventPropagation.All)
|
||||||
{
|
{
|
||||||
_handled = false;
|
_handled = false;
|
||||||
|
|
||||||
switch (propagation)
|
switch (propagation)
|
||||||
{
|
{
|
||||||
case EventPropagation.All:
|
case EventPropagation.All:
|
||||||
// 触发所有处理器
|
// 使用快照避免迭代期间修改
|
||||||
foreach (var handler in _handlers)
|
// 合并所有处理器并按优先级排序
|
||||||
|
var allHandlersForAll = _handlers
|
||||||
|
.Select(h => (h.Priority, Handler: (Action?)(() => h.Handler.Invoke(t)),
|
||||||
|
ContextHandler: (Action<EventContext<T>>?)null, IsContext: false))
|
||||||
|
.Concat(_contextHandlers
|
||||||
|
.Select(h => (h.Priority, Handler: (Action?)null,
|
||||||
|
ContextHandler: (Action<EventContext<T>>?)h.Handler, IsContext: true)))
|
||||||
|
.OrderByDescending(h => h.Priority)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var contextAll = new EventContext<T>(t);
|
||||||
|
foreach (var item in allHandlersForAll)
|
||||||
{
|
{
|
||||||
handler.Handler.Invoke(t);
|
if (item.IsContext)
|
||||||
|
{
|
||||||
|
item.ContextHandler?.Invoke(contextAll);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Handler?.Invoke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EventPropagation.UntilHandled:
|
case EventPropagation.UntilHandled:
|
||||||
// 触发直到某个处理器标记为已处理
|
// 合并所有处理器并按优先级排序
|
||||||
foreach (var handler in _handlers)
|
var allHandlers = _handlers
|
||||||
|
.Select(h => (h.Priority, Handler: (Action?)(() => h.Handler.Invoke(t)),
|
||||||
|
ContextHandler: (Action<EventContext<T>>?)null, IsContext: false))
|
||||||
|
.Concat(_contextHandlers
|
||||||
|
.Select(h => (h.Priority, Handler: (Action?)null,
|
||||||
|
ContextHandler: (Action<EventContext<T>>?)h.Handler, IsContext: true)))
|
||||||
|
.OrderByDescending(h => h.Priority)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var context = new EventContext<T>(t);
|
||||||
|
foreach (var item in allHandlers)
|
||||||
{
|
{
|
||||||
handler.Handler.Invoke(t);
|
if (item.IsContext)
|
||||||
if (_handled) break;
|
{
|
||||||
|
item.ContextHandler?.Invoke(context);
|
||||||
|
if (context.IsHandled) break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Handler?.Invoke();
|
||||||
|
if (_handled) break; // 保持向后兼容
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EventPropagation.Highest:
|
case EventPropagation.Highest:
|
||||||
// 仅触发最高优先级的处理器
|
// 使用快照避免迭代期间修改
|
||||||
if (_handlers.Count > 0)
|
var normalSnapshot = _handlers.ToArray();
|
||||||
|
var contextSnapshot = _contextHandlers.ToArray();
|
||||||
|
|
||||||
|
// 找到最高优先级
|
||||||
|
var highestPriority = int.MinValue;
|
||||||
|
if (normalSnapshot.Length > 0)
|
||||||
|
highestPriority = Math.Max(highestPriority, normalSnapshot[0].Priority);
|
||||||
|
if (contextSnapshot.Length > 0)
|
||||||
|
highestPriority = Math.Max(highestPriority, contextSnapshot[0].Priority);
|
||||||
|
|
||||||
|
if (highestPriority != int.MinValue)
|
||||||
{
|
{
|
||||||
var highestPriority = _handlers[0].Priority;
|
// 触发最高优先级的普通处理器
|
||||||
foreach (var handler in _handlers)
|
foreach (var handler in normalSnapshot)
|
||||||
{
|
{
|
||||||
if (handler.Priority < highestPriority) break;
|
if (handler.Priority < highestPriority) break;
|
||||||
handler.Handler.Invoke(t);
|
handler.Handler.Invoke(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 触发最高优先级的上下文处理器
|
||||||
|
var contextHighest = new EventContext<T>(t);
|
||||||
|
foreach (var handler in contextSnapshot)
|
||||||
|
{
|
||||||
|
if (handler.Priority < highestPriority) break;
|
||||||
|
handler.Handler.Invoke(contextHighest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标记事件为已处理(用于 UntilHandled 传播模式)
|
/// 事件处理器包装类,包含处理器和优先级
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void MarkAsHandled()
|
private sealed class EventHandler(Action<T> handler, int priority)
|
||||||
{
|
{
|
||||||
_handled = true;
|
public Action<T> Handler { get; } = handler;
|
||||||
|
public int Priority { get; } = priority;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上下文事件处理器包装类,包含处理器和优先级
|
||||||
|
/// </summary>
|
||||||
|
private sealed class ContextEventHandler(Action<EventContext<T>> handler, int priority)
|
||||||
|
{
|
||||||
|
public Action<EventContext<T>> Handler { get; } = handler;
|
||||||
|
public int Priority { get; } = priority;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ public static class SpanExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">目标类型,必须实现 ISpanParsable 接口</typeparam>
|
/// <typeparam name="T">目标类型,必须实现 ISpanParsable 接口</typeparam>
|
||||||
/// <param name="span">要解析的字符 span</param>
|
/// <param name="span">要解析的字符 span</param>
|
||||||
/// <param name="result">解析结果</param>
|
/// <param name="result">解析结果,失败时为 default(T)</param>
|
||||||
/// <returns>如果解析成功返回 true,否则返回 false</returns>
|
/// <returns>如果解析成功返回 true,否则返回 false</returns>
|
||||||
/// <example>
|
/// <example>
|
||||||
/// <code>
|
/// <code>
|
||||||
@ -21,7 +21,7 @@ public static class SpanExtensions
|
|||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static bool TryParseValue<T>(this ReadOnlySpan<char> span, out T? result) where T : ISpanParsable<T>
|
public static bool TryParseValue<T>(this ReadOnlySpan<char> span, out T result) where T : ISpanParsable<T>
|
||||||
{
|
{
|
||||||
return T.TryParse(span, null, out result);
|
return T.TryParse(span, null, out result);
|
||||||
}
|
}
|
||||||
@ -47,6 +47,7 @@ public static class SpanExtensions
|
|||||||
if (item.Equals(value))
|
if (item.Equals(value))
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,25 +363,25 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// <returns>服务实例或null</returns>
|
/// <returns>服务实例或null</returns>
|
||||||
public T? Get<T>() where T : class
|
public T? Get<T>() where T : class
|
||||||
{
|
{
|
||||||
if (_provider == null)
|
|
||||||
{
|
|
||||||
// 如果容器未冻结,从服务集合中查找已注册的实例
|
|
||||||
var serviceType = typeof(T);
|
|
||||||
var descriptor = GetServicesUnsafe.FirstOrDefault(s =>
|
|
||||||
s.ServiceType == serviceType || serviceType.IsAssignableFrom(s.ServiceType));
|
|
||||||
|
|
||||||
if (descriptor?.ImplementationInstance is T instance)
|
|
||||||
{
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在未冻结状态下无法调用工厂方法或创建实例,返回null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock.EnterReadLock();
|
_lock.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_provider == null)
|
||||||
|
{
|
||||||
|
// 如果容器未冻结,从服务集合中查找已注册的实例
|
||||||
|
var serviceType = typeof(T);
|
||||||
|
var descriptor = GetServicesUnsafe.FirstOrDefault(s =>
|
||||||
|
s.ServiceType == serviceType || serviceType.IsAssignableFrom(s.ServiceType));
|
||||||
|
|
||||||
|
if (descriptor?.ImplementationInstance is T instance)
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在未冻结状态下无法调用工厂方法或创建实例,返回null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var result = _provider!.GetService<T>();
|
var result = _provider!.GetService<T>();
|
||||||
_logger.Debug(result != null
|
_logger.Debug(result != null
|
||||||
? $"Retrieved instance: {typeof(T).Name}"
|
? $"Retrieved instance: {typeof(T).Name}"
|
||||||
@ -402,18 +402,19 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// <returns>服务实例或null</returns>
|
/// <returns>服务实例或null</returns>
|
||||||
public object? Get(Type type)
|
public object? Get(Type type)
|
||||||
{
|
{
|
||||||
if (_provider == null)
|
|
||||||
{
|
|
||||||
// 如果容器未冻结,从服务集合中查找已注册的实例
|
|
||||||
var descriptor =
|
|
||||||
GetServicesUnsafe.FirstOrDefault(s => s.ServiceType == type || type.IsAssignableFrom(s.ServiceType));
|
|
||||||
|
|
||||||
return descriptor?.ImplementationInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock.EnterReadLock();
|
_lock.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_provider == null)
|
||||||
|
{
|
||||||
|
// 如果容器未冻结,从服务集合中查找已注册的实例
|
||||||
|
var descriptor =
|
||||||
|
GetServicesUnsafe.FirstOrDefault(s =>
|
||||||
|
s.ServiceType == type || type.IsAssignableFrom(s.ServiceType));
|
||||||
|
|
||||||
|
return descriptor?.ImplementationInstance;
|
||||||
|
}
|
||||||
|
|
||||||
var result = _provider!.GetService(type);
|
var result = _provider!.GetService(type);
|
||||||
_logger.Debug(result != null
|
_logger.Debug(result != null
|
||||||
? $"Retrieved instance: {type.Name}"
|
? $"Retrieved instance: {type.Name}"
|
||||||
@ -491,36 +492,36 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// <returns>只读的服务实例列表</returns>
|
/// <returns>只读的服务实例列表</returns>
|
||||||
public IReadOnlyList<T> GetAll<T>() where T : class
|
public IReadOnlyList<T> GetAll<T>() where T : class
|
||||||
{
|
{
|
||||||
if (_provider == null)
|
|
||||||
{
|
|
||||||
// 如果容器未冻结,从服务集合中获取已注册的实例
|
|
||||||
var serviceType = typeof(T);
|
|
||||||
var registeredServices = GetServicesUnsafe
|
|
||||||
.Where(s => s.ServiceType == serviceType || serviceType.IsAssignableFrom(s.ServiceType)).ToList();
|
|
||||||
|
|
||||||
var result = new List<T>();
|
|
||||||
foreach (var descriptor in registeredServices)
|
|
||||||
{
|
|
||||||
if (descriptor.ImplementationInstance is T instance)
|
|
||||||
{
|
|
||||||
result.Add(instance);
|
|
||||||
}
|
|
||||||
else if (descriptor.ImplementationFactory != null)
|
|
||||||
{
|
|
||||||
// 在未冻结状态下无法调用工厂方法,跳过
|
|
||||||
}
|
|
||||||
else if (descriptor.ImplementationType != null)
|
|
||||||
{
|
|
||||||
// 在未冻结状态下无法创建实例,跳过
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock.EnterReadLock();
|
_lock.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_provider == null)
|
||||||
|
{
|
||||||
|
// 如果容器未冻结,从服务集合中获取已注册的实例
|
||||||
|
var serviceType = typeof(T);
|
||||||
|
var registeredServices = GetServicesUnsafe
|
||||||
|
.Where(s => s.ServiceType == serviceType || serviceType.IsAssignableFrom(s.ServiceType)).ToList();
|
||||||
|
|
||||||
|
var result = new List<T>();
|
||||||
|
foreach (var descriptor in registeredServices)
|
||||||
|
{
|
||||||
|
if (descriptor.ImplementationInstance is T instance)
|
||||||
|
{
|
||||||
|
result.Add(instance);
|
||||||
|
}
|
||||||
|
else if (descriptor.ImplementationFactory != null)
|
||||||
|
{
|
||||||
|
// 在未冻结状态下无法调用工厂方法,跳过
|
||||||
|
}
|
||||||
|
else if (descriptor.ImplementationType != null)
|
||||||
|
{
|
||||||
|
// 在未冻结状态下无法创建实例,跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
var services = _provider!.GetServices<T>().ToList();
|
var services = _provider!.GetServices<T>().ToList();
|
||||||
_logger.Debug($"Retrieved {services.Count} instances of {typeof(T).Name}");
|
_logger.Debug($"Retrieved {services.Count} instances of {typeof(T).Name}");
|
||||||
return services;
|
return services;
|
||||||
@ -539,36 +540,36 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
|
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
|
||||||
public IReadOnlyList<object> GetAll(Type type)
|
public IReadOnlyList<object> GetAll(Type type)
|
||||||
{
|
{
|
||||||
if (_provider == null)
|
|
||||||
{
|
|
||||||
// 如果容器未冻结,从服务集合中获取已注册的实例
|
|
||||||
var registeredServices = GetServicesUnsafe
|
|
||||||
.Where(s => s.ServiceType == type || type.IsAssignableFrom(s.ServiceType))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var result = new List<object>();
|
|
||||||
foreach (var descriptor in registeredServices)
|
|
||||||
{
|
|
||||||
if (descriptor.ImplementationInstance != null)
|
|
||||||
{
|
|
||||||
result.Add(descriptor.ImplementationInstance);
|
|
||||||
}
|
|
||||||
else if (descriptor.ImplementationFactory != null)
|
|
||||||
{
|
|
||||||
// 在未冻结状态下无法调用工厂方法,跳过
|
|
||||||
}
|
|
||||||
else if (descriptor.ImplementationType != null)
|
|
||||||
{
|
|
||||||
// 在未冻结状态下无法创建实例,跳过
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock.EnterReadLock();
|
_lock.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_provider == null)
|
||||||
|
{
|
||||||
|
// 如果容器未冻结,从服务集合中获取已注册的实例
|
||||||
|
var registeredServices = GetServicesUnsafe
|
||||||
|
.Where(s => s.ServiceType == type || type.IsAssignableFrom(s.ServiceType))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var result = new List<object>();
|
||||||
|
foreach (var descriptor in registeredServices)
|
||||||
|
{
|
||||||
|
if (descriptor.ImplementationInstance != null)
|
||||||
|
{
|
||||||
|
result.Add(descriptor.ImplementationInstance);
|
||||||
|
}
|
||||||
|
else if (descriptor.ImplementationFactory != null)
|
||||||
|
{
|
||||||
|
// 在未冻结状态下无法调用工厂方法,跳过
|
||||||
|
}
|
||||||
|
else if (descriptor.ImplementationType != null)
|
||||||
|
{
|
||||||
|
// 在未冻结状态下无法创建实例,跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
var services = _provider!.GetServices(type).ToList();
|
var services = _provider!.GetServices(type).ToList();
|
||||||
_logger.Debug($"Retrieved {services.Count} instances of {type.Name}");
|
_logger.Debug($"Retrieved {services.Count} instances of {type.Name}");
|
||||||
return services.Where(o => o != null).Cast<object>().ToList();
|
return services.Where(o => o != null).Cast<object>().ToList();
|
||||||
@ -605,12 +606,12 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// <returns>true表示包含该类型实例,false表示不包含</returns>
|
/// <returns>true表示包含该类型实例,false表示不包含</returns>
|
||||||
public bool Contains<T>() where T : class
|
public bool Contains<T>() where T : class
|
||||||
{
|
{
|
||||||
if (_provider == null)
|
|
||||||
return GetServicesUnsafe.Any(s => s.ServiceType == typeof(T));
|
|
||||||
|
|
||||||
_lock.EnterReadLock();
|
_lock.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_provider == null)
|
||||||
|
return GetServicesUnsafe.Any(s => s.ServiceType == typeof(T));
|
||||||
|
|
||||||
return _provider.GetService<T>() != null;
|
return _provider.GetService<T>() != null;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -706,16 +707,17 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
|
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
|
||||||
public IServiceScope CreateScope()
|
public IServiceScope CreateScope()
|
||||||
{
|
{
|
||||||
if (_provider == null)
|
|
||||||
{
|
|
||||||
const string errorMsg = "Cannot create scope before container is frozen";
|
|
||||||
_logger.Error(errorMsg);
|
|
||||||
throw new InvalidOperationException(errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock.EnterReadLock();
|
_lock.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// 在锁内检查,避免竞态条件
|
||||||
|
if (!_frozen || _provider == null)
|
||||||
|
{
|
||||||
|
const string errorMsg = "Cannot create scope before container is frozen";
|
||||||
|
_logger.Error(errorMsg);
|
||||||
|
throw new InvalidOperationException(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
var scope = _provider.CreateScope();
|
var scope = _provider.CreateScope();
|
||||||
_logger.Debug("Service scope created");
|
_logger.Debug("Service scope created");
|
||||||
return scope;
|
return scope;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user