- 新增 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。
147 lines
4.4 KiB
C#
147 lines
4.4 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
|
|
namespace ShrinkEventBus
|
|
{
|
|
public abstract class EventBase : IDisposable
|
|
{
|
|
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;
|
|
|
|
[IgnoreDataMember]
|
|
public EventHandlerInfo? CurrentHandler { get; internal set; }
|
|
|
|
[IgnoreDataMember]
|
|
public DateTime EventTime { get; private set; } = DateTime.UtcNow;
|
|
|
|
[IgnoreDataMember]
|
|
public Guid EventId => _eventId ??= Guid.NewGuid();
|
|
|
|
[IgnoreDataMember]
|
|
public bool IsCancelable { get; }
|
|
|
|
[IgnoreDataMember]
|
|
public bool HasResult { get; }
|
|
|
|
[IgnoreDataMember]
|
|
public EventPriority? Phase { get; private set; }
|
|
|
|
internal bool IsInPool { get; set; }
|
|
internal Action<EventBase>? ReleaseAction { get; set; }
|
|
|
|
protected EventBase()
|
|
{
|
|
var metadata = GetOrCreateMetadata(GetType());
|
|
IsCancelable = metadata.IsCancelable;
|
|
HasResult = metadata.HasResult;
|
|
Setup();
|
|
}
|
|
|
|
protected virtual void Setup() { }
|
|
|
|
protected virtual void OnReset() { }
|
|
|
|
internal void ResetInternal()
|
|
{
|
|
_isCanceled = false;
|
|
_result = EventResult.DEFAULT;
|
|
CurrentHandler = null;
|
|
Phase = null;
|
|
EventTime = DateTime.UtcNow;
|
|
_eventId = null;
|
|
_dispatchSnapshot = null;
|
|
OnReset();
|
|
}
|
|
|
|
internal void PrepareForDispatch()
|
|
{
|
|
CurrentHandler = null;
|
|
Phase = null;
|
|
EventTime = DateTime.UtcNow;
|
|
_eventId = null;
|
|
_dispatchSnapshot = null;
|
|
}
|
|
|
|
internal void SetListenerSnapshot(EventHandlerInfo[]? handlers)
|
|
{
|
|
_dispatchSnapshot = handlers is { Length: > 0 } ? handlers : null;
|
|
}
|
|
|
|
[IgnoreDataMember]
|
|
public bool IsCanceled
|
|
{
|
|
get => _isCanceled;
|
|
set
|
|
{
|
|
if (!IsCancelable)
|
|
throw new UnsupportedOperationException(
|
|
$"Event {GetType().Name} is not cancelable. Mark it with [Cancelable] to allow cancellation.");
|
|
_isCanceled = value;
|
|
}
|
|
}
|
|
|
|
[IgnoreDataMember]
|
|
public EventResult Result
|
|
{
|
|
get => _result;
|
|
set
|
|
{
|
|
if (!HasResult)
|
|
throw new InvalidOperationException(
|
|
$"Event {GetType().Name} does not support results. Mark it with [HasResult] to allow setting a result.");
|
|
_result = value;
|
|
}
|
|
}
|
|
|
|
internal void SetPhase(EventPriority value)
|
|
{
|
|
if (Phase == value) return;
|
|
if (Phase != null && Phase.Value.CompareTo(value) > 0)
|
|
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 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
|
|
});
|
|
}
|
|
}
|
|
}
|