mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +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.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 readonly Dictionary<Type, object> _mockContextServices = new();
|
||||
|
||||
/// <summary>
|
||||
/// 测试注册单例实例的功能
|
||||
@ -249,12 +248,18 @@ public class MicrosoftDiContainerTests
|
||||
[Test]
|
||||
public void Contains_WithExistingInstance_Should_ReturnTrue()
|
||||
{
|
||||
// 使用 RegisterSingleton 方法来避免与其他测试方法重复
|
||||
var instance = new TestService();
|
||||
_container.Register(instance);
|
||||
|
||||
_container.RegisterSingleton(instance);
|
||||
|
||||
// 验证容器包含该实例
|
||||
Assert.That(_container.Contains<TestService>(), Is.True);
|
||||
// 验证实例确实是单例
|
||||
Assert.That(_container.Get<TestService>(), Is.SameAs(instance));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 测试当不存在实例时检查包含关系应返回 false 的功能
|
||||
/// </summary>
|
||||
@ -327,6 +332,107 @@ public class MicrosoftDiContainerTests
|
||||
|
||||
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>
|
||||
|
||||
@ -78,4 +78,27 @@ public class EventBus : IEventBus
|
||||
{
|
||||
_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
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件处理器包装类,包含处理器和优先级
|
||||
/// 存储已注册的上下文事件处理器列表
|
||||
/// </summary>
|
||||
private class EventHandler
|
||||
{
|
||||
public Action<T> Handler { get; }
|
||||
public int Priority { get; }
|
||||
|
||||
public EventHandler(Action<T> handler, int priority)
|
||||
{
|
||||
Handler = handler;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
private readonly List<ContextEventHandler> _contextHandlers = new();
|
||||
|
||||
/// <summary>
|
||||
/// 存储已注册的事件处理器列表
|
||||
@ -80,12 +70,29 @@ public class PriorityEvent<T> : IEvent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发所有已注册的事件处理程序(默认传播模式:All)
|
||||
/// 注册一个上下文事件监听器,并指定优先级
|
||||
/// </summary>
|
||||
/// <param name="t">传递给事件处理程序的参数</param>
|
||||
public void Trigger(T t)
|
||||
/// <param name="onEvent">要注册的事件处理方法,接收 EventContext 参数</param>
|
||||
/// <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>
|
||||
@ -93,49 +100,116 @@ public class PriorityEvent<T> : IEvent
|
||||
/// </summary>
|
||||
/// <param name="t">传递给事件处理程序的参数</param>
|
||||
/// <param name="propagation">事件传播模式</param>
|
||||
public void Trigger(T t, EventPropagation propagation)
|
||||
public void Trigger(T t, EventPropagation propagation = EventPropagation.All)
|
||||
{
|
||||
_handled = false;
|
||||
|
||||
switch (propagation)
|
||||
{
|
||||
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;
|
||||
|
||||
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 (_handled) break;
|
||||
if (item.IsContext)
|
||||
{
|
||||
item.ContextHandler?.Invoke(context);
|
||||
if (context.IsHandled) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Handler?.Invoke();
|
||||
if (_handled) break; // 保持向后兼容
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记事件为已处理(用于 UntilHandled 传播模式)
|
||||
/// 事件处理器包装类,包含处理器和优先级
|
||||
/// </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>
|
||||
/// <typeparam name="T">目标类型,必须实现 ISpanParsable 接口</typeparam>
|
||||
/// <param name="span">要解析的字符 span</param>
|
||||
/// <param name="result">解析结果</param>
|
||||
/// <param name="result">解析结果,失败时为 default(T)</param>
|
||||
/// <returns>如果解析成功返回 true,否则返回 false</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
@ -21,7 +21,7 @@ public static class SpanExtensions
|
||||
/// }
|
||||
/// </code>
|
||||
/// </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);
|
||||
}
|
||||
@ -47,6 +47,7 @@ public static class SpanExtensions
|
||||
if (item.Equals(value))
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -363,25 +363,25 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
/// <returns>服务实例或null</returns>
|
||||
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();
|
||||
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>();
|
||||
_logger.Debug(result != null
|
||||
? $"Retrieved instance: {typeof(T).Name}"
|
||||
@ -402,18 +402,19 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
/// <returns>服务实例或null</returns>
|
||||
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();
|
||||
try
|
||||
{
|
||||
if (_provider == null)
|
||||
{
|
||||
// 如果容器未冻结,从服务集合中查找已注册的实例
|
||||
var descriptor =
|
||||
GetServicesUnsafe.FirstOrDefault(s =>
|
||||
s.ServiceType == type || type.IsAssignableFrom(s.ServiceType));
|
||||
|
||||
return descriptor?.ImplementationInstance;
|
||||
}
|
||||
|
||||
var result = _provider!.GetService(type);
|
||||
_logger.Debug(result != null
|
||||
? $"Retrieved instance: {type.Name}"
|
||||
@ -491,36 +492,36 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
/// <returns>只读的服务实例列表</returns>
|
||||
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();
|
||||
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();
|
||||
_logger.Debug($"Retrieved {services.Count} instances of {typeof(T).Name}");
|
||||
return services;
|
||||
@ -539,36 +540,36 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
|
||||
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();
|
||||
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();
|
||||
_logger.Debug($"Retrieved {services.Count} instances of {type.Name}");
|
||||
return services.Where(o => o != null).Cast<object>().ToList();
|
||||
@ -605,12 +606,12 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
/// <returns>true表示包含该类型实例,false表示不包含</returns>
|
||||
public bool Contains<T>() where T : class
|
||||
{
|
||||
if (_provider == null)
|
||||
return GetServicesUnsafe.Any(s => s.ServiceType == typeof(T));
|
||||
|
||||
_lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (_provider == null)
|
||||
return GetServicesUnsafe.Any(s => s.ServiceType == typeof(T));
|
||||
|
||||
return _provider.GetService<T>() != null;
|
||||
}
|
||||
finally
|
||||
@ -706,16 +707,17 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
|
||||
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();
|
||||
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();
|
||||
_logger.Debug("Service scope created");
|
||||
return scope;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user