feat(events): 添加事件优先级和传播控制功能

- 实现了事件优先级机制,支持按优先级顺序执行事件处理器
- 新增EventPropagation枚举,支持All、UntilHandled和Highest三种传播模式
- 添加Register方法的重载版本,支持指定事件处理器优先级
- 实现Send方法的重载版本,支持指定事件传播模式
- 新增PriorityEvent类处理带优先级的事件逻辑
- 添加IocContainer的CreateScope方法支持服务作用域管理
- 实现RegisterTransient和RegisterScoped方法完善依赖注入生命周期
- 新增SpanExtensions扩展方法提供高性能的span操作功能
- 添加全面的单元测试覆盖事件优先级、IoC容器生命周期和span扩展功能
This commit is contained in:
GeWuYou 2026-02-25 13:32:13 +08:00 committed by gewuyou
parent e2cfa7bffa
commit e2dca4f5a6
10 changed files with 955 additions and 1 deletions

View File

@ -0,0 +1,22 @@
namespace GFramework.Core.Abstractions.events;
/// <summary>
/// 事件传播模式
/// </summary>
public enum EventPropagation
{
/// <summary>
/// 传播到所有处理器
/// </summary>
All,
/// <summary>
/// 传播直到某个处理器标记为已处理
/// </summary>
UntilHandled,
/// <summary>
/// 仅传播到最高优先级的处理器
/// </summary>
Highest
}

View File

@ -18,6 +18,14 @@ public interface IEventBus
/// <param name="e">事件实例</param> /// <param name="e">事件实例</param>
void Send<T>(T e); void Send<T>(T e);
/// <summary>
/// 发送指定的事件实例,并指定传播模式
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="e">事件实例</param>
/// <param name="propagation">事件传播模式</param>
void Send<T>(T e, EventPropagation propagation);
/// <summary> /// <summary>
/// 注册事件监听器 /// 注册事件监听器
/// </summary> /// </summary>
@ -26,6 +34,15 @@ public interface IEventBus
/// <returns>反注册接口,用于注销事件监听</returns> /// <returns>反注册接口,用于注销事件监听</returns>
IUnRegister Register<T>(Action<T> onEvent); IUnRegister Register<T>(Action<T> onEvent);
/// <summary>
/// 注册事件监听器,并指定优先级
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="onEvent">事件处理回调函数</param>
/// <param name="priority">优先级,数值越大优先级越高</param>
/// <returns>反注册接口,用于注销事件监听</returns>
IUnRegister Register<T>(Action<T> onEvent, int priority);
/// <summary> /// <summary>
/// 注销事件监听器 /// 注销事件监听器
/// </summary> /// </summary>

View File

@ -29,6 +29,24 @@ public interface IIocContainer : IContextAware
void RegisterSingleton<TService, TImpl>() void RegisterSingleton<TService, TImpl>()
where TImpl : class, TService where TService : class; where TImpl : class, TService where TService : class;
/// <summary>
/// 注册瞬态服务,指定服务类型和实现类型
/// 每次解析时都会创建新的实例
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
void RegisterTransient<TService, TImpl>()
where TImpl : class, TService where TService : class;
/// <summary>
/// 注册作用域服务,指定服务类型和实现类型
/// 在同一作用域内共享实例,不同作用域使用不同实例
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
void RegisterScoped<TService, TImpl>()
where TImpl : class, TService where TService : class;
/// <summary> /// <summary>
/// 注册多个实例 /// 注册多个实例
/// 将实例注册到其实现的所有接口和具体类型上 /// 将实例注册到其实现的所有接口和具体类型上
@ -183,5 +201,12 @@ public interface IIocContainer : IContextAware
/// <returns>底层的IServiceCollection实例</returns> /// <returns>底层的IServiceCollection实例</returns>
IServiceCollection GetServicesUnsafe { get; } IServiceCollection GetServicesUnsafe { get; }
/// <summary>
/// 创建一个新的服务作用域
/// 作用域内的 Scoped 服务将共享同一实例
/// </summary>
/// <returns>服务作用域实例</returns>
IServiceScope CreateScope();
#endregion #endregion
} }

View File

@ -0,0 +1,237 @@
using GFramework.Core.Abstractions.events;
using GFramework.Core.events;
using NUnit.Framework;
namespace GFramework.Core.Tests.events;
/// <summary>
/// 测试事件系统优先级和传播控制功能
/// </summary>
[TestFixture]
public class EventBusPriorityTests
{
private class TestEvent
{
public string Message { get; set; } = string.Empty;
}
[Test]
public void Register_With_Priority_Should_Execute_In_Priority_Order()
{
// Arrange
var eventBus = new EventBus();
var executionOrder = new List<int>();
eventBus.Register<TestEvent>(_ => executionOrder.Add(1), priority: 1);
eventBus.Register<TestEvent>(_ => executionOrder.Add(3), priority: 3);
eventBus.Register<TestEvent>(_ => executionOrder.Add(2), priority: 2);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
// Assert
Assert.That(executionOrder, Is.EqualTo(new[] { 3, 2, 1 }));
}
[Test]
public void Register_Without_Priority_Should_Use_Default_Priority()
{
// Arrange
var eventBus = new EventBus();
var executionOrder = new List<string>();
eventBus.Register<TestEvent>(_ => executionOrder.Add("default"), priority: 0);
eventBus.Register<TestEvent>(_ => executionOrder.Add("high"), priority: 10);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
// Assert
Assert.That(executionOrder[0], Is.EqualTo("high"));
Assert.That(executionOrder[1], Is.EqualTo("default"));
}
[Test]
public void Send_With_Propagation_All_Should_Execute_All_Handlers()
{
// Arrange
var eventBus = new EventBus();
var executionCount = 0;
eventBus.Register<TestEvent>(_ => executionCount++, priority: 1);
eventBus.Register<TestEvent>(_ => executionCount++, priority: 2);
eventBus.Register<TestEvent>(_ => executionCount++, priority: 3);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
// Assert
Assert.That(executionCount, Is.EqualTo(3));
}
[Test]
public void Send_With_Propagation_Highest_Should_Execute_Only_Highest_Priority()
{
// Arrange
var eventBus = new EventBus();
var executionOrder = new List<int>();
eventBus.Register<TestEvent>(_ => executionOrder.Add(1), priority: 1);
eventBus.Register<TestEvent>(_ => executionOrder.Add(3), priority: 3);
eventBus.Register<TestEvent>(_ => executionOrder.Add(2), priority: 2);
// Act
eventBus.Send(new TestEvent(), EventPropagation.Highest);
// Assert
Assert.That(executionOrder.Count, Is.EqualTo(1));
Assert.That(executionOrder[0], Is.EqualTo(3));
}
[Test]
public void Send_With_Propagation_Highest_Should_Execute_All_With_Same_Highest_Priority()
{
// Arrange
var eventBus = new EventBus();
var executionOrder = new List<string>();
eventBus.Register<TestEvent>(_ => executionOrder.Add("high1"), priority: 10);
eventBus.Register<TestEvent>(_ => executionOrder.Add("high2"), priority: 10);
eventBus.Register<TestEvent>(_ => executionOrder.Add("low"), priority: 1);
// Act
eventBus.Send(new TestEvent(), EventPropagation.Highest);
// Assert
Assert.That(executionOrder.Count, Is.EqualTo(2));
Assert.That(executionOrder, Does.Contain("high1"));
Assert.That(executionOrder, Does.Contain("high2"));
Assert.That(executionOrder, Does.Not.Contain("low"));
}
[Test]
public void Negative_Priority_Should_Work_Correctly()
{
// Arrange
var eventBus = new EventBus();
var executionOrder = new List<int>();
eventBus.Register<TestEvent>(_ => executionOrder.Add(-1), priority: -1);
eventBus.Register<TestEvent>(_ => executionOrder.Add(0), priority: 0);
eventBus.Register<TestEvent>(_ => executionOrder.Add(1), priority: 1);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
// Assert
Assert.That(executionOrder, Is.EqualTo(new[] { 1, 0, -1 }));
}
[Test]
public void Multiple_Events_Should_Maintain_Independent_Priorities()
{
// Arrange
var eventBus = new EventBus();
var event1Order = new List<int>();
var event2Order = new List<int>();
eventBus.Register<TestEvent>(_ => event1Order.Add(1), priority: 1);
eventBus.Register<TestEvent>(_ => event1Order.Add(2), priority: 2);
eventBus.Register<string>(_ => event2Order.Add(10), priority: 10);
eventBus.Register<string>(_ => event2Order.Add(20), priority: 20);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
eventBus.Send("test", EventPropagation.All);
// Assert
Assert.That(event1Order, Is.EqualTo(new[] { 2, 1 }));
Assert.That(event2Order, Is.EqualTo(new[] { 20, 10 }));
}
[Test]
public void UnRegister_Should_Remove_Handler_From_Priority_List()
{
// Arrange
var eventBus = new EventBus();
var executionCount = 0;
Action<TestEvent> handler = _ => executionCount++;
var unregister = eventBus.Register(handler, priority: 5);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
unregister.UnRegister();
eventBus.Send(new TestEvent(), EventPropagation.All);
// Assert
Assert.That(executionCount, Is.EqualTo(1));
}
[Test]
public void Send_Without_Propagation_Should_Use_Default_Event_System()
{
// Arrange
var eventBus = new EventBus();
var executionCount = 0;
// 使用默认注册(无优先级)
eventBus.Register<TestEvent>(_ => executionCount++);
// Act
eventBus.Send(new TestEvent()); // 不指定传播模式
// Assert
Assert.That(executionCount, Is.EqualTo(1));
}
[Test]
public void Priority_Event_And_Normal_Event_Should_Be_Independent()
{
// Arrange
var eventBus = new EventBus();
var normalCount = 0;
var priorityCount = 0;
eventBus.Register<TestEvent>(_ => normalCount++);
eventBus.Register<TestEvent>(_ => priorityCount++, priority: 1);
// Act
eventBus.Send(new TestEvent()); // 触发普通事件
eventBus.Send(new TestEvent(), EventPropagation.All); // 触发优先级事件
// Assert
Assert.That(normalCount, Is.EqualTo(1));
Assert.That(priorityCount, Is.EqualTo(1));
}
[Test]
public void Empty_Event_Bus_Should_Not_Throw_Exception()
{
// Arrange
var eventBus = new EventBus();
// Act & Assert
Assert.DoesNotThrow(() => eventBus.Send(new TestEvent(), EventPropagation.All));
Assert.DoesNotThrow(() => eventBus.Send(new TestEvent(), EventPropagation.Highest));
}
[Test]
public void Same_Priority_Handlers_Should_Execute_In_Registration_Order()
{
// Arrange
var eventBus = new EventBus();
var executionOrder = new List<string>();
eventBus.Register<TestEvent>(_ => executionOrder.Add("first"), priority: 5);
eventBus.Register<TestEvent>(_ => executionOrder.Add("second"), priority: 5);
eventBus.Register<TestEvent>(_ => executionOrder.Add("third"), priority: 5);
// Act
eventBus.Send(new TestEvent(), EventPropagation.All);
// Assert
Assert.That(executionOrder, Is.EqualTo(new[] { "first", "second", "third" }));
}
}

View File

@ -0,0 +1,152 @@
using GFramework.Core.extensions;
using NUnit.Framework;
namespace GFramework.Core.Tests.extensions;
/// <summary>
/// 测试 SpanExtensions 扩展方法的功能
/// </summary>
[TestFixture]
public class SpanExtensionsTests
{
[Test]
public void TryParseValue_Should_Parse_Valid_Integer()
{
// Arrange
ReadOnlySpan<char> span = "123";
// Act
var success = span.TryParseValue<int>(out var result);
// Assert
Assert.That(success, Is.True);
Assert.That(result, Is.EqualTo(123));
}
[Test]
public void TryParseValue_Should_Fail_For_Invalid_Integer()
{
// Arrange
ReadOnlySpan<char> span = "abc";
// Act
var success = span.TryParseValue<int>(out var result);
// Assert
Assert.That(success, Is.False);
Assert.That(result, Is.EqualTo(0));
}
[Test]
public void TryParseValue_Should_Parse_Valid_Double()
{
// Arrange
ReadOnlySpan<char> span = "123.45";
// Act
var success = span.TryParseValue<double>(out var result);
// Assert
Assert.That(success, Is.True);
Assert.That(result, Is.EqualTo(123.45).Within(0.001));
}
[Test]
public void TryParseValue_Should_Parse_Valid_Boolean()
{
// Arrange
ReadOnlySpan<char> span = "true";
// Act
var success = span.TryParseValue<bool>(out var result);
// Assert
Assert.That(success, Is.True);
Assert.That(result, Is.True);
}
[Test]
public void TryParseValue_Should_Parse_Valid_Guid()
{
// Arrange
var guid = Guid.NewGuid();
ReadOnlySpan<char> span = guid.ToString();
// Act
var success = span.TryParseValue<Guid>(out var result);
// Assert
Assert.That(success, Is.True);
Assert.That(result, Is.EqualTo(guid));
}
[Test]
public void CountOccurrences_Should_Return_Correct_Count()
{
// Arrange
ReadOnlySpan<int> span = stackalloc int[] { 1, 2, 3, 2, 1 };
// Act
var count = span.CountOccurrences(2);
// Assert
Assert.That(count, Is.EqualTo(2));
}
[Test]
public void CountOccurrences_Should_Return_Zero_When_Value_Not_Found()
{
// Arrange
ReadOnlySpan<int> span = stackalloc int[] { 1, 2, 3 };
// Act
var count = span.CountOccurrences(5);
// Assert
Assert.That(count, Is.EqualTo(0));
}
[Test]
public void CountOccurrences_Should_Return_Zero_For_Empty_Span()
{
// Arrange
ReadOnlySpan<int> span = ReadOnlySpan<int>.Empty;
// Act
var count = span.CountOccurrences(1);
// Assert
Assert.That(count, Is.EqualTo(0));
}
[Test]
public void CountOccurrences_With_Chars_Should_Work()
{
// Arrange
ReadOnlySpan<char> span = "hello";
// Act
var count = span.CountOccurrences('l');
// Assert
Assert.That(count, Is.EqualTo(2));
}
[Test]
public void CountOccurrences_Should_Work_With_Custom_Types()
{
// Arrange
var item1 = new TestItem(1);
var item2 = new TestItem(2);
var item3 = new TestItem(2);
ReadOnlySpan<TestItem> span = new[] { item1, item2, item3 };
// Act
var count = span.CountOccurrences(new TestItem(2));
// Assert
Assert.That(count, Is.EqualTo(2));
}
private record TestItem(int Value);
}

View File

@ -0,0 +1,208 @@
using GFramework.Core.ioc;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
namespace GFramework.Core.Tests.ioc;
/// <summary>
/// 测试 IoC 容器生命周期功能
/// </summary>
[TestFixture]
public class IocContainerLifetimeTests
{
private interface ITestService
{
Guid Id { get; }
}
private class TestService : ITestService
{
public Guid Id { get; } = Guid.NewGuid();
}
[Test]
public void RegisterSingleton_Should_Return_Same_Instance()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterSingleton<ITestService, TestService>();
container.Freeze();
// Act
var instance1 = container.Get<ITestService>();
var instance2 = container.Get<ITestService>();
// Assert
Assert.That(instance1, Is.Not.Null);
Assert.That(instance2, Is.Not.Null);
Assert.That(instance1!.Id, Is.EqualTo(instance2!.Id));
}
[Test]
public void RegisterTransient_Should_Return_Different_Instances()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterTransient<ITestService, TestService>();
container.Freeze();
// Act
var instance1 = container.Get<ITestService>();
var instance2 = container.Get<ITestService>();
// Assert
Assert.That(instance1, Is.Not.Null);
Assert.That(instance2, Is.Not.Null);
Assert.That(instance1!.Id, Is.Not.EqualTo(instance2!.Id));
}
[Test]
public void RegisterScoped_Should_Return_Same_Instance_Within_Scope()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterScoped<ITestService, TestService>();
container.Freeze();
// Act
using var scope = container.CreateScope();
var instance1 = scope.ServiceProvider.GetService<ITestService>();
var instance2 = scope.ServiceProvider.GetService<ITestService>();
// Assert
Assert.That(instance1, Is.Not.Null);
Assert.That(instance2, Is.Not.Null);
Assert.That(instance1!.Id, Is.EqualTo(instance2!.Id));
}
[Test]
public void RegisterScoped_Should_Return_Different_Instances_Across_Scopes()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterScoped<ITestService, TestService>();
container.Freeze();
// Act
ITestService? instance1;
ITestService? instance2;
using (var scope1 = container.CreateScope())
{
instance1 = scope1.ServiceProvider.GetService<ITestService>();
}
using (var scope2 = container.CreateScope())
{
instance2 = scope2.ServiceProvider.GetService<ITestService>();
}
// Assert
Assert.That(instance1, Is.Not.Null);
Assert.That(instance2, Is.Not.Null);
Assert.That(instance1!.Id, Is.Not.EqualTo(instance2!.Id));
}
[Test]
public void CreateScope_Should_Throw_When_Container_Not_Frozen()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterScoped<ITestService, TestService>();
// Act & Assert
Assert.Throws<InvalidOperationException>(() => container.CreateScope());
}
[Test]
public void RegisterTransient_Should_Throw_When_Container_Is_Frozen()
{
// Arrange
var container = new MicrosoftDiContainer();
container.Freeze();
// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
container.RegisterTransient<ITestService, TestService>());
}
[Test]
public void RegisterScoped_Should_Throw_When_Container_Is_Frozen()
{
// Arrange
var container = new MicrosoftDiContainer();
container.Freeze();
// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
container.RegisterScoped<ITestService, TestService>());
}
[Test]
public void Mixed_Lifetimes_Should_Work_Together()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterSingleton<ITestService, TestService>();
container.RegisterTransient<ITestService, TestService>();
container.RegisterScoped<ITestService, TestService>();
container.Freeze();
// Act
var singletonInstances = container.GetAll<ITestService>().ToList();
// Assert
Assert.That(singletonInstances.Count, Is.EqualTo(3));
}
[Test]
public void Scoped_Service_Should_Be_Disposed_When_Scope_Disposed()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterScoped<ITestService, TestService>();
container.Freeze();
ITestService? instance;
using (var scope = container.CreateScope())
{
instance = scope.ServiceProvider.GetService<ITestService>();
Assert.That(instance, Is.Not.Null);
}
// Act & Assert - 作用域已释放,实例应该被清理
// 注意:这里只是验证作用域可以正常释放,无法直接验证实例是否被 Dispose
Assert.Pass("Scope disposed successfully");
}
[Test]
public void Multiple_Scopes_Can_Be_Created_Concurrently()
{
// Arrange
var container = new MicrosoftDiContainer();
container.RegisterScoped<ITestService, TestService>();
container.Freeze();
// Act
var scope1 = container.CreateScope();
var scope2 = container.CreateScope();
var scope3 = container.CreateScope();
var instance1 = scope1.ServiceProvider.GetService<ITestService>();
var instance2 = scope2.ServiceProvider.GetService<ITestService>();
var instance3 = scope3.ServiceProvider.GetService<ITestService>();
// Assert
Assert.That(instance1, Is.Not.Null);
Assert.That(instance2, Is.Not.Null);
Assert.That(instance3, Is.Not.Null);
Assert.That(instance1!.Id, Is.Not.EqualTo(instance2!.Id));
Assert.That(instance2!.Id, Is.Not.EqualTo(instance3!.Id));
Assert.That(instance1!.Id, Is.Not.EqualTo(instance3!.Id));
// Cleanup
scope1.Dispose();
scope2.Dispose();
scope3.Dispose();
}
}

View File

@ -8,6 +8,7 @@ namespace GFramework.Core.events;
public class EventBus : IEventBus public class EventBus : IEventBus
{ {
private readonly EasyEvents _mEvents = new(); private readonly EasyEvents _mEvents = new();
private readonly EasyEvents _mPriorityEvents = new();
/// <summary> /// <summary>
/// 发送事件,自动创建事件实例 /// 发送事件,自动创建事件实例
@ -32,6 +33,19 @@ public class EventBus : IEventBus
.Trigger(e); .Trigger(e);
} }
/// <summary>
/// 发送指定的事件实例,并指定传播模式
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="e">事件实例</param>
/// <param name="propagation">事件传播模式</param>
public void Send<T>(T e, EventPropagation propagation)
{
_mPriorityEvents
.GetOrAddEvent<PriorityEvent<T>>()
.Trigger(e, propagation);
}
/// <summary> /// <summary>
/// 注册事件监听器 /// 注册事件监听器
/// </summary> /// </summary>
@ -43,6 +57,18 @@ public class EventBus : IEventBus
return _mEvents.GetOrAddEvent<Event<T>>().Register(onEvent); 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)
{
return _mPriorityEvents.GetOrAddEvent<PriorityEvent<T>>().Register(onEvent, priority);
}
/// <summary> /// <summary>
/// 注销事件监听器 /// 注销事件监听器
/// </summary> /// </summary>

View File

@ -0,0 +1,141 @@
using GFramework.Core.Abstractions.events;
namespace GFramework.Core.events;
/// <summary>
/// 支持优先级的泛型事件类
/// </summary>
/// <typeparam name="T">事件回调函数的参数类型</typeparam>
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;
}
}
/// <summary>
/// 存储已注册的事件处理器列表
/// </summary>
private readonly List<EventHandler> _handlers = new();
/// <summary>
/// 标记事件是否已被处理(用于 UntilHandled 传播模式)
/// </summary>
private bool _handled;
/// <summary>
/// 显式实现 IEvent 接口中的 Register 方法
/// </summary>
/// <param name="onEvent">无参事件处理方法</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器</returns>
IUnRegister IEvent.Register(Action onEvent)
{
return Register(_ => onEvent(), 0);
}
/// <summary>
/// 注册一个事件监听器,默认优先级为 0
/// </summary>
/// <param name="onEvent">要注册的事件处理方法</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器</returns>
public IUnRegister Register(Action<T> onEvent)
{
return Register(onEvent, 0);
}
/// <summary>
/// 注册一个事件监听器,并指定优先级
/// </summary>
/// <param name="onEvent">要注册的事件处理方法</param>
/// <param name="priority">优先级,数值越大优先级越高</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器</returns>
public IUnRegister Register(Action<T> onEvent, int priority)
{
var handler = new EventHandler(onEvent, priority);
_handlers.Add(handler);
// 按优先级降序排序(高优先级在前)
_handlers.Sort((a, b) => b.Priority.CompareTo(a.Priority));
return new DefaultUnRegister(() => UnRegister(onEvent));
}
/// <summary>
/// 取消指定的事件监听器
/// </summary>
/// <param name="onEvent">需要被注销的事件处理方法</param>
public void UnRegister(Action<T> onEvent)
{
_handlers.RemoveAll(h => h.Handler == onEvent);
}
/// <summary>
/// 触发所有已注册的事件处理程序默认传播模式All
/// </summary>
/// <param name="t">传递给事件处理程序的参数</param>
public void Trigger(T t)
{
Trigger(t, EventPropagation.All);
}
/// <summary>
/// 触发事件处理程序,并指定传播模式
/// </summary>
/// <param name="t">传递给事件处理程序的参数</param>
/// <param name="propagation">事件传播模式</param>
public void Trigger(T t, EventPropagation propagation)
{
_handled = false;
switch (propagation)
{
case EventPropagation.All:
// 触发所有处理器
foreach (var handler in _handlers)
{
handler.Handler.Invoke(t);
}
break;
case EventPropagation.UntilHandled:
// 触发直到某个处理器标记为已处理
foreach (var handler in _handlers)
{
handler.Handler.Invoke(t);
if (_handled) break;
}
break;
case EventPropagation.Highest:
// 仅触发最高优先级的处理器
if (_handlers.Count > 0)
{
var highestPriority = _handlers[0].Priority;
foreach (var handler in _handlers)
{
if (handler.Priority < highestPriority) break;
handler.Handler.Invoke(t);
}
}
break;
}
}
/// <summary>
/// 标记事件为已处理(用于 UntilHandled 传播模式)
/// </summary>
public void MarkAsHandled()
{
_handled = true;
}
}

View File

@ -0,0 +1,52 @@
namespace GFramework.Core.extensions;
/// <summary>
/// Span 和 ReadOnlySpan 扩展方法,提供零分配的高性能操作
/// </summary>
public static class SpanExtensions
{
/// <summary>
/// 尝试将字符 span 解析为指定类型
/// </summary>
/// <typeparam name="T">目标类型,必须实现 ISpanParsable 接口</typeparam>
/// <param name="span">要解析的字符 span</param>
/// <param name="result">解析结果</param>
/// <returns>如果解析成功返回 true否则返回 false</returns>
/// <example>
/// <code>
/// ReadOnlySpan&lt;char&gt; span = "123";
/// if (span.TryParseValue&lt;int&gt;(out var result))
/// {
/// Console.WriteLine(result); // 123
/// }
/// </code>
/// </example>
public static bool TryParseValue<T>(this ReadOnlySpan<char> span, out T? result) where T : ISpanParsable<T>
{
return T.TryParse(span, null, out result);
}
/// <summary>
/// 计算 span 中指定值出现的次数
/// </summary>
/// <typeparam name="T">元素类型,必须实现 IEquatable 接口</typeparam>
/// <param name="span">要搜索的 span</param>
/// <param name="value">要计数的值</param>
/// <returns>值出现的次数</returns>
/// <example>
/// <code>
/// ReadOnlySpan&lt;int&gt; span = stackalloc int[] { 1, 2, 3, 2, 1 };
/// var count = span.CountOccurrences(2); // 2
/// </code>
/// </example>
public static int CountOccurrences<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>
{
var count = 0;
foreach (var item in span)
{
if (item.Equals(value))
count++;
}
return count;
}
}

View File

@ -120,6 +120,52 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
} }
} }
/// <summary>
/// 注册瞬态服务,指定服务类型和实现类型
/// 每次解析时都会创建新的实例
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
public void RegisterTransient<TService, TImpl>()
where TImpl : class, TService
where TService : class
{
_lock.EnterWriteLock();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddTransient<TService, TImpl>();
_logger.Debug($"Transient registered: {typeof(TService).Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册作用域服务,指定服务类型和实现类型
/// 在同一作用域内共享实例,不同作用域使用不同实例
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
public void RegisterScoped<TService, TImpl>()
where TImpl : class, TService
where TService : class
{
_lock.EnterWriteLock();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddScoped<TService, TImpl>();
_logger.Debug($"Scoped registered: {typeof(TService).Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary> /// <summary>
/// 注册多个实例到其所有接口和具体类型 /// 注册多个实例到其所有接口和具体类型
@ -291,7 +337,7 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
/// 配置服务 /// 配置服务
/// </summary> /// </summary>
/// <param name="configurator">服务配置委托</param> /// <param name="configurator">服务配置委托</param>
public void ExecuteServicesHook(Action<IServiceCollection>? configurator) public void ExecuteServicesHook(Action<IServiceCollection>? configurator = null)
{ {
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
@ -652,5 +698,33 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
/// <returns>底层的IServiceCollection实例</returns> /// <returns>底层的IServiceCollection实例</returns>
public IServiceCollection GetServicesUnsafe { get; } = serviceCollection ?? new ServiceCollection(); public IServiceCollection GetServicesUnsafe { get; } = serviceCollection ?? new ServiceCollection();
/// <summary>
/// 创建一个新的服务作用域
/// 作用域内的 Scoped 服务将共享同一实例
/// </summary>
/// <returns>服务作用域实例</returns>
/// <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
{
var scope = _provider.CreateScope();
_logger.Debug("Service scope created");
return scope;
}
finally
{
_lock.ExitReadLock();
}
}
#endregion #endregion
} }