using System; using System.Collections.Generic; using System.Reflection; namespace ShrinkEventBus { internal static class EventCloneUtility { private static readonly object CacheLock = new(); private static readonly Dictionary FieldCache = new(); public static TEvent CloneForDetachedDispatch(TEvent source) where TEvent : EventBase { if (source == null) throw new ArgumentNullException(nameof(source)); if (Activator.CreateInstance(source.GetType()) is not TEvent clone) throw new InvalidOperationException( $"Cannot clone event type {source.GetType().FullName}. A public parameterless constructor is required."); // 先固化 EventId,让克隆与原事件共享同一个派发标识 _ = source.EventId; CopyFields(source, clone); clone.ReleaseAction = null; clone.IsInPool = false; return clone; } private static void CopyFields(EventBase source, EventBase target) { var fields = GetCopyableFields(source.GetType()); for (var i = 0; i < fields.Length; i++) fields[i].SetValue(target, fields[i].GetValue(source)); } private static FieldInfo[] GetCopyableFields(Type type) { lock (CacheLock) { if (FieldCache.TryGetValue(type, out var cached)) return cached; var fields = new List(); var currentType = type; while (currentType != null && currentType != typeof(object)) { var declaredFields = currentType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); for (var i = 0; i < declaredFields.Length; i++) { var field = declaredFields[i]; if (field.IsStatic) continue; if (ShouldSkipField(field)) continue; fields.Add(field); } currentType = currentType.BaseType; } cached = fields.ToArray(); FieldCache[type] = cached; return cached; } } private static bool ShouldSkipField(FieldInfo field) { return field.Name is "k__BackingField" or "k__BackingField"; } } }