feat: 重构事件总线为可实例化架构并发布 1.3.0
- 新增 IShrinkEventBus、ShrinkEventBusBuilder 与 ShrinkEventBusInstance,支持独立 bus、异常策略、启动开关、类型校验和按 phase 派发 - 新增 SubscribeEvent 可释放订阅句柄与订阅快照接口,保留 EventBus 静态门面并委托到默认实例 - 重构监听列表为父子事件链共享锁与快照缓存,支持父事件监听子事件、跨父子数字优先级归并和防御性快照拷贝 - 优化 EventBase 元数据缓存、懒生成 EventId、派发快照挂接与对象池双重归还保护,移除旧 EventCache 热路径 - 恢复本地 ILPostProcessor 兜底并生成静态订阅注册表,避免共享 CodeGen 覆盖外程序集自动注册失效 - 改进事件查看器、Benchmark、README 与 CHANGELOG,并新增 Dispatch/Priority/Registration/Subscription 测试覆盖核心行为 BREAKING CHANGE: 移除 EventBase.GetListenerList(),int 数字优先级重载不再提供默认值,数字优先级 0 现在映射为 NORMAL。
This commit is contained in:
41 files changed
+3168
-888
No files matched your search
+54
-15
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
@@ -7,7 +8,16 @@ namespace ShrinkEventBus
|
||||
{
|
||||
public abstract class EventBase : IDisposable
|
||||
{
|
||||
private readonly ListenerList _listenerList = new();
|
||||
private sealed class EventMetadata
|
||||
{
|
||||
public bool IsCancelable;
|
||||
public bool HasResult;
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, EventMetadata> MetadataCache = new();
|
||||
|
||||
private EventHandlerInfo[]? _dispatchSnapshot;
|
||||
private Guid? _eventId;
|
||||
private bool _isCanceled;
|
||||
private EventResult _result = EventResult.DEFAULT;
|
||||
|
||||
@@ -18,7 +28,7 @@ namespace ShrinkEventBus
|
||||
public DateTime EventTime { get; private set; } = DateTime.UtcNow;
|
||||
|
||||
[IgnoreDataMember]
|
||||
public Guid EventId { get; private set; } = Guid.NewGuid();
|
||||
public Guid EventId => _eventId ??= Guid.NewGuid();
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool IsCancelable { get; }
|
||||
@@ -34,13 +44,14 @@ namespace ShrinkEventBus
|
||||
|
||||
protected EventBase()
|
||||
{
|
||||
IsCancelable = GetType().GetCustomAttribute<CancelableAttribute>() != null;
|
||||
HasResult = GetType().GetCustomAttribute<HasResultAttribute>() != null;
|
||||
var metadata = GetOrCreateMetadata(GetType());
|
||||
IsCancelable = metadata.IsCancelable;
|
||||
HasResult = metadata.HasResult;
|
||||
Setup();
|
||||
}
|
||||
|
||||
protected virtual void Setup() { }
|
||||
|
||||
|
||||
protected virtual void OnReset() { }
|
||||
|
||||
internal void ResetInternal()
|
||||
@@ -50,8 +61,8 @@ namespace ShrinkEventBus
|
||||
CurrentHandler = null;
|
||||
Phase = null;
|
||||
EventTime = DateTime.UtcNow;
|
||||
EventId = Guid.NewGuid();
|
||||
_listenerList.Clear();
|
||||
_eventId = null;
|
||||
_dispatchSnapshot = null;
|
||||
OnReset();
|
||||
}
|
||||
|
||||
@@ -60,8 +71,13 @@ namespace ShrinkEventBus
|
||||
CurrentHandler = null;
|
||||
Phase = null;
|
||||
EventTime = DateTime.UtcNow;
|
||||
EventId = Guid.NewGuid();
|
||||
_listenerList.Clear();
|
||||
_eventId = null;
|
||||
_dispatchSnapshot = null;
|
||||
}
|
||||
|
||||
internal void SetListenerSnapshot(EventHandlerInfo[]? handlers)
|
||||
{
|
||||
_dispatchSnapshot = handlers is { Length: > 0 } ? handlers : null;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
@@ -70,7 +86,9 @@ namespace ShrinkEventBus
|
||||
get => _isCanceled;
|
||||
set
|
||||
{
|
||||
if (!IsCancelable) throw new UnsupportedOperationException();
|
||||
if (!IsCancelable)
|
||||
throw new UnsupportedOperationException(
|
||||
$"Event {GetType().Name} is not cancelable. Mark it with [Cancelable] to allow cancellation.");
|
||||
_isCanceled = value;
|
||||
}
|
||||
}
|
||||
@@ -81,7 +99,9 @@ namespace ShrinkEventBus
|
||||
get => _result;
|
||||
set
|
||||
{
|
||||
if (!HasResult) throw new InvalidOperationException();
|
||||
if (!HasResult)
|
||||
throw new InvalidOperationException(
|
||||
$"Event {GetType().Name} does not support results. Mark it with [HasResult] to allow setting a result.");
|
||||
_result = value;
|
||||
}
|
||||
}
|
||||
@@ -90,18 +110,37 @@ namespace ShrinkEventBus
|
||||
{
|
||||
if (Phase == value) return;
|
||||
if (Phase != null && Phase.Value.CompareTo(value) > 0)
|
||||
throw new ArgumentException();
|
||||
throw new ArgumentException(
|
||||
$"Event phase cannot move backwards from {Phase.Value} to {value}.", nameof(value));
|
||||
Phase = value;
|
||||
}
|
||||
|
||||
public void SetCanceled(bool canceled) => IsCanceled = canceled;
|
||||
public void SetResult(EventResult result) => Result = result;
|
||||
public ListenerList GetListenerList() => _listenerList;
|
||||
public EventHandlerInfo[] GetSubscribers() => _listenerList.GetHandlers();
|
||||
|
||||
public EventHandlerInfo[] GetSubscribers()
|
||||
{
|
||||
var snapshot = _dispatchSnapshot;
|
||||
if (snapshot == null || snapshot.Length == 0)
|
||||
return Array.Empty<EventHandlerInfo>();
|
||||
|
||||
var copy = new EventHandlerInfo[snapshot.Length];
|
||||
Array.Copy(snapshot, copy, snapshot.Length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseAction?.Invoke(this);
|
||||
}
|
||||
|
||||
private static EventMetadata GetOrCreateMetadata(Type eventType)
|
||||
{
|
||||
return MetadataCache.GetOrAdd(eventType, static type => new EventMetadata
|
||||
{
|
||||
IsCancelable = type.GetCustomAttribute<CancelableAttribute>() != null,
|
||||
HasResult = type.GetCustomAttribute<HasResultAttribute>() != null
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user