组件生命周期管理器架构:ECS事件处理的优雅实践

在ECS(Entity-Component-System)架构中,组件是纯粹的数据容器,系统负责处理数据。然而实际开发中,当组件发生变更(添加、移除、更新)时,往往需要触发额外的业务逻辑------例如更新UI、启动异步任务或同步网络状态。传统的轮询方式不仅低效,还会导致代码耦合。本文介绍一个专为ECS设计的组件生命周期管理器,它通过事件驱动和清晰的层次划分,让组件事件处理变得简单、灵活且高性能。

  1. 设计目标
    解耦:组件生产者(事件源)与消费者(处理器)仅通过接口交互,双方互不感知。

高性能:优先使用泛型事件,配合缓存和表达式树编译,最大程度减少反射和装箱开销。

灵活执行:支持同步、线程池、UI线程三种执行模式,适应不同场景需求。

线程安全:在多线程环境下安全地进行注册、注销和事件分发。

易扩展:可通过适配器接入任意实现了标准接口的事件源。

  1. 核心组件
    整个框架围绕以下几个核心组件构建:

2.1 事件源接口与适配器

框架定义了两层事件源:基础的非泛型接口提供组件添加、移除、更新的非泛型事件,以及按类型获取组件的方法;泛型接口在其基础上增加了泛型订阅方法,允许直接获得强类型组件,避免装箱。为了统一处理这两种事件源,适配器模式被引入------适配器封装了非泛型事件源,并提供获取组件和检测泛型源的能力。默认适配器实现通过表达式树编译缓存了泛型获取组件的方法,确保非泛型调用也能高效工作。

2.2 处理器接口

用户通过实现泛型接口来定义自己的业务逻辑,接口包含三个默认方法:组件添加时调用、组件移除时调用、组件更新时调用(携带新旧值)。由于提供了默认实现,用户只需重写需要关注的方法即可。

2.3 执行策略

不同的业务场景可能需要不同的执行方式:UI更新必须在UI线程执行,耗时计算可以扔到线程池,而实时性要求高的逻辑应立即同步执行。框架通过执行模式枚举和执行策略接口来抽象这一需求。默认执行策略根据模式在当前线程、线程池或捕获的UI同步上下文上执行任务,并自动捕获异常,避免单个处理器的崩溃影响整个系统。

2.4 核心管理器

生命周期管理器是整个框架的心脏,它负责协调上述所有组件:

处理器管理:维护每个组件类型对应的处理器列表(支持多线程安全更新),并提供注册和注销方法。

事件订阅:订阅事件源的泛型或非泛型事件,优先使用泛型以获得最佳性能;若事件源不支持泛型,则通过适配器回退到非泛型事件。

组件缓存:为每个组件类型维护线程安全的缓存,存储实体当前关联的组件值,以便在非泛型事件中也能快速提供新旧值。

执行调度:根据注册时指定的执行模式,通过执行策略异步或同步地调用处理器。

资源管理:实现IDisposable接口,取消所有订阅、清空缓存,并等待正在执行的处理器完成,确保资源正确释放。

  1. 工作流程简述
    初始化:创建事件源适配器和生命周期管理器,可传入自定义执行策略或使用默认策略。

注册处理器:调用管理器的注册方法,将实现了处理器接口的实例与特定组件类型绑定,并指定执行模式。

事件触发:当外部事件源触发组件添加/移除/更新时,管理器接收到通知:

如果是泛型事件,直接获得强类型组件,更新缓存并调用对应处理器。

如果是非泛型事件,通过适配器获取组件对象,更新缓存,然后通过动态生成的委托调用处理器,并传入转换后的强类型参数。

执行调度:每个处理器调用会被包装在执行策略中,按照指定的模式执行,确保不会阻塞事件源线程(如果需要异步)。

资源释放:调用Dispose取消所有订阅,清空缓存,释放资源。

  1. 使用示例
    以下是一个完整的示例,演示如何利用该框架监听组件生命周期事件,代码行数严格控制在50行以内。
csharp 复制代码
// 1. 定义实体与组件
public readonly record struct Entity(int Id);
public record struct Position(float X, float Y);

// 2. 实现处理器
public class LoggingHandler : IComponentLifecycleHandler<Position>
{
    public void OnAdded(Entity e, Position p) => 
        Console.WriteLine($"[Added] Entity {e.Id} at ({p.X}, {p.Y})");
    
    public void OnChanged(Entity e, Position newP, Position oldP) =>
        Console.WriteLine($"[Changed] Entity {e.Id}: ({oldP.X},{oldP.Y}) -> ({newP.X},{newP.Y})");
}

// 3. 模拟事件源(非泛型,用于演示适配器)
class MockEventSource : IComponentEventSource
{
    public event Action<Entity, Type>? ComponentAdded, ComponentRemoved;
    public event Action<Entity, Type, object?, object>? ComponentUpdated;
    public T GetComponent<T>(Entity entity) where T : struct => throw new NotImplementedException();
    public bool IsEntityValid(Entity entity) => true;
    public void RaiseAdd(Entity e, Position p) => ComponentAdded?.Invoke(e, typeof(Position));
    public void RaiseUpdate(Entity e, Position oldP, Position newP) => 
        ComponentUpdated?.Invoke(e, typeof(Position), oldP, newP);
}

// 4. 使用示例
public static void Main()
{
    // 创建事件源和适配器
    var source = new MockEventSource();
    var adapter = new ComponentEventSourceAdapter(source);
    
    // 创建管理器,使用同步执行策略
    using var manager = new ComponentLifecycleManager(adapter);
    
    // 注册处理器
    manager.RegisterHandler<Position>(new LoggingHandler(), ExecutionMode.Sync);
    
    // 模拟组件添加事件
    var entity = new Entity(42);
    source.RaiseAdd(entity, new Position(10, 20));
    
    // 模拟组件更新事件
    source.RaiseUpdate(entity, new Position(10, 20), new Position(15, 25));
    
    // 输出:
    // [Added] Entity 42 at (10, 20)
    // [Changed] Entity 42: (10,20) -> (15,25)
}

说明:

第1-2行定义了实体类型和组件(值类型)。

第4-12行实现了一个简单的日志处理器,关注添加和更新事件。

第14-22行模拟了一个最小化的非泛型事件源,用于手动触发事件。

第24-38行展示了完整流程:创建适配器、管理器、注册处理器、触发事件,最终输出预期结果。

该示例完整展示了框架的核心用法,所有代码均可直接运行,无需依赖任何外部库。

  1. 总结
    组件生命周期管理器为ECS架构提供了一种解耦、高效且灵活的事件处理方案。通过清晰的接口分层、泛型优先的设计、可插拔的执行策略以及完善的资源管理,开发者可以轻松地监听组件变化并执行相应的业务逻辑,同时保持代码的简洁和可维护性。无论是游戏开发、实时仿真还是数据驱动应用,该框架都能成为你工具箱中得力的助手。
csharp 复制代码
namespace ComponentLifecycle
{
    // ========== 接口定义(保持不变)==========
    public enum ExecutionMode { Sync, ThreadPool, UI }

    public interface IComponentEventSource
    {
        event Action<Entity, Type> ComponentAdded;
        event Action<Entity, Type> ComponentRemoved;
        event Action<Entity, Type, object?, object> ComponentUpdated;
        T GetComponent<T>(Entity entity) where T : struct;
        bool IsEntityValid(Entity entity);
    }

    public interface IGenericComponentEventSource : IComponentEventSource
    {
        void SubscribeAdded<T>(Action<Entity, T> handler) where T : struct;
        void UnsubscribeAdded<T>(Action<Entity, T> handler) where T : struct;
        void SubscribeRemoved<T>(Action<Entity, T> handler) where T : struct;
        void UnsubscribeRemoved<T>(Action<Entity, T> handler) where T : struct;
        void SubscribeUpdated<T>(Action<Entity, T, T> handler) where T : struct;
        void UnsubscribeUpdated<T>(Action<Entity, T, T> handler) where T : struct;
    }

    public interface IComponentLifecycleHandler<T> where T : struct
    {
        void OnAdded(Entity entity, T component) { }
        void OnRemoved(Entity entity, T component) { }
        void OnChanged(Entity entity, T newComponent, T oldComponent) { }
    }

    public interface IComponentEventSourceAdapter
    {
        event Action<Entity, Type> ComponentAdded;
        event Action<Entity, Type> ComponentRemoved;
        event Action<Entity, Type, object?, object> ComponentUpdated;
        object? GetComponent(Entity entity, Type componentType);
        IGenericComponentEventSource? AsGeneric();
    }

    public interface IExecutionStrategy
    {
        void Execute(Action action, ExecutionMode mode);
    }

    // ========== 默认实现 ==========
    public class ComponentEventSourceAdapter : IComponentEventSourceAdapter
    {
        readonly IComponentEventSource _source;
        readonly IGenericComponentEventSource? _genericSource;
        static readonly ConcurrentDictionary<Type, Func<IComponentEventSource, Entity, object>> _getterCache = new();

        public ComponentEventSourceAdapter(IComponentEventSource source) => (_source, _genericSource) = (source ?? throw new ArgumentNullException(nameof(source)), source as IGenericComponentEventSource);

        public event Action<Entity, Type> ComponentAdded { add => _source.ComponentAdded += value; remove => _source.ComponentAdded -= value; }
        public event Action<Entity, Type> ComponentRemoved { add => _source.ComponentRemoved += value; remove => _source.ComponentRemoved -= value; }
        public event Action<Entity, Type, object?, object> ComponentUpdated { add => _source.ComponentUpdated += value; remove => _source.ComponentUpdated -= value; }

        public object? GetComponent(Entity entity, Type componentType)
        {
            var getter = _getterCache.GetOrAdd(componentType, t =>
            {
                var method = typeof(IComponentEventSource).GetMethod(nameof(IComponentEventSource.GetComponent))!.MakeGenericMethod(t);
                var s = Expression.Parameter(typeof(IComponentEventSource), "s");
                var e = Expression.Parameter(typeof(Entity), "e");
                return Expression.Lambda<Func<IComponentEventSource, Entity, object>>(Expression.Convert(Expression.Call(s, method, e), typeof(object)), s, e).Compile();
            });
            return getter(_source, entity);
        }

        public IGenericComponentEventSource? AsGeneric() => _genericSource;
    }

    public class DefaultExecutionStrategy : IExecutionStrategy
    {
        readonly SynchronizationContext _uiContext;
        public DefaultExecutionStrategy(SynchronizationContext uiContext) => _uiContext = uiContext ?? throw new ArgumentNullException(nameof(uiContext));
        public void Execute(Action action, ExecutionMode mode)
        {
            void Safe() { try { action(); } catch { } }
            switch (mode)
            {
                case ExecutionMode.Sync: Safe(); break;
                case ExecutionMode.ThreadPool: ThreadPool.QueueUserWorkItem(_ => Safe()); break;
                case ExecutionMode.UI: _uiContext.Post(_ => Safe(), null); break;
            }
        }
    }

    // ========== 核心管理器(简化版)==========
    public class ComponentLifecycleManager : IDisposable
    {
        readonly IComponentEventSourceAdapter _adapter;
        readonly bool _disposeHandlersOnShutdown;
        readonly IExecutionStrategy _executor;
        readonly Dictionary<Type, ImmutableList<HandlerEntry>> _handlers = new();
        readonly ReaderWriterLockSlim _lock = new();
        readonly ConcurrentDictionary<Type, MethodDelegates> _handlerMethodDelegates = new();
        readonly Dictionary<Type, SubscriptionInfo> _genericSubscriptionInfos = new();
        readonly HashSet<Type> _genericSubscribedTypes = new();
        readonly ConcurrentDictionary<Type, IComponentTypeCache> _componentCaches = new();
        int _executingCount;
        bool _subscribed;
        bool _disposed;

        interface IComponentTypeCache { object? Get(Entity entity); void Set(Entity entity, object component); bool Remove(Entity entity); }
        class ComponentTypeCache<T> : IComponentTypeCache, IDisposable where T : struct
        {
            readonly Dictionary<Entity, T> _cache = new();
            readonly ReaderWriterLockSlim _lock = new();
            public object? Get(Entity entity) { _lock.EnterReadLock(); try { return _cache.TryGetValue(entity, out var v) ? v : null; } finally { _lock.ExitReadLock(); } }
            public void Set(Entity entity, object component) { _lock.EnterWriteLock(); try { _cache[(Entity)entity] = (T)component; } finally { _lock.ExitWriteLock(); } }
            public bool Remove(Entity entity) { _lock.EnterWriteLock(); try { return _cache.Remove(entity); } finally { _lock.ExitWriteLock(); } }
            public void Dispose() => _lock.Dispose();
        }
        record HandlerEntry(object Handler, ExecutionMode Mode);
        record SubscriptionInfo(Type ComponentType, Delegate Added, Delegate Removed, Delegate Updated);
        record MethodDelegates(Action<object, Entity, object> Added, Action<object, Entity, object> Removed, Action<object, Entity, object, object> Changed);

        static readonly MethodInfo OnAddedMethodDef = typeof(IComponentLifecycleHandler<>).GetMethod("OnAdded", BindingFlags.Public | BindingFlags.Instance)!;
        static readonly MethodInfo OnRemovedMethodDef = typeof(IComponentLifecycleHandler<>).GetMethod("OnRemoved", BindingFlags.Public | BindingFlags.Instance)!;
        static readonly MethodInfo OnChangedMethodDef = typeof(IComponentLifecycleHandler<>).GetMethod("OnChanged", BindingFlags.Public | BindingFlags.Instance)!;

        public ComponentLifecycleManager(IComponentEventSourceAdapter adapter, bool disposeHandlersOnShutdown = false, IExecutionStrategy? executor = null)
        {
            _adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
            _disposeHandlersOnShutdown = disposeHandlersOnShutdown;
            _executor = executor ?? new DefaultExecutionStrategy(SynchronizationContext.Current ?? new SynchronizationContext());
        }
        public ComponentLifecycleManager(IComponentEventSource eventSource, SynchronizationContext? uiContext = null, bool disposeHandlersOnShutdown = false)
            : this(new ComponentEventSourceAdapter(eventSource), disposeHandlersOnShutdown, uiContext == null ? null : new DefaultExecutionStrategy(uiContext)) { }
        public ComponentLifecycleManager(IGenericComponentEventSource eventSource, SynchronizationContext? uiContext = null, bool disposeHandlersOnShutdown = false)
            : this(new ComponentEventSourceAdapter(eventSource), disposeHandlersOnShutdown, uiContext == null ? null : new DefaultExecutionStrategy(uiContext)) { }

        public void RegisterHandler<T>(IComponentLifecycleHandler<T> handler, ExecutionMode mode = ExecutionMode.Sync) where T : struct
        {
            if (handler == null) throw new ArgumentNullException(nameof(handler));
            CheckDisposed();
            var type = typeof(T);
            using (Lock.Write(_lock))
            {
                _handlers[type] = (_handlers.TryGetValue(type, out var list) ? list : ImmutableList<HandlerEntry>.Empty).Add(new(handler, mode));
                if (_handlers[type].Count == 1 && _adapter.AsGeneric() is { } generic)
                {
                    var (added, removed, updated) = (new Action<Entity, T>(OnAddedGeneric), new Action<Entity, T>(OnRemovedGeneric), new Action<Entity, T, T>(OnUpdatedGeneric));
                    generic.SubscribeAdded(added); generic.SubscribeRemoved(removed); generic.SubscribeUpdated(updated);
                    _genericSubscriptionInfos[type] = new(type, added, removed, updated);
                    _genericSubscribedTypes.Add(type);
                }
                if (!_subscribed)
                {
                    _adapter.ComponentAdded += OnComponentAddedNonGeneric;
                    _adapter.ComponentRemoved += OnComponentRemovedNonGeneric;
                    _adapter.ComponentUpdated += OnComponentUpdatedNonGeneric;
                    _subscribed = true;
                }
            }
        }

        public void UnregisterHandler<T>(IComponentLifecycleHandler<T> handler) where T : struct
        {
            if (handler == null) throw new ArgumentNullException(nameof(handler));
            CheckDisposed();
            if (!_subscribed) return;
            var type = typeof(T);
            using (Lock.Write(_lock))
            {
                if (!_handlers.TryGetValue(type, out var list)) return;
                var newList = list.RemoveAll(e => ReferenceEquals(e.Handler, handler));
                if (newList.IsEmpty)
                {
                    _handlers.Remove(type);
                    _genericSubscribedTypes.Remove(type);
                    if (_genericSubscriptionInfos.TryGetValue(type, out var info) && _adapter.AsGeneric() is { } generic)
                    {
                        if (info.Added is Action<Entity, T> a) generic.UnsubscribeAdded(a);
                        if (info.Removed is Action<Entity, T> r) generic.UnsubscribeRemoved(r);
                        if (info.Updated is Action<Entity, T, T> u) generic.UnsubscribeUpdated(u);
                        _genericSubscriptionInfos.Remove(type);
                    }
                }
                else _handlers[type] = newList;
            }
        }

        void OnAddedGeneric<T>(Entity entity, T component) where T : struct { if (_disposed) return; GetCacheForType<T>().Set(entity, component); ForEachHandler(entity, component, (h, e, c) => h.OnAdded(e, c)); }
        void OnRemovedGeneric<T>(Entity entity, T component) where T : struct { if (_disposed) return; GetCacheForType<T>().Remove(entity); ForEachHandler(entity, component, (h, e, c) => h.OnRemoved(e, c)); }
        void OnUpdatedGeneric<T>(Entity entity, T newComponent, T oldComponent) where T : struct { if (_disposed) return; GetCacheForType<T>().Set(entity, newComponent); ForEachHandler(entity, newComponent, (h, e, _) => h.OnChanged(e, newComponent, oldComponent)); }

        void ForEachHandler<T>(Entity entity, T component, Action<IComponentLifecycleHandler<T>, Entity, T> invoke) where T : struct
        {
            if (TryGetHandlers(typeof(T)) is not { } list) return;
            foreach (var e in list)
                if (e.Handler is IComponentLifecycleHandler<T> h)
                {
                    Interlocked.Increment(ref _executingCount);
                    _executor.Execute(() => { try { invoke(h, entity, component); } finally { Interlocked.Decrement(ref _executingCount); } }, e.Mode);
                }
        }

        void OnComponentAddedNonGeneric(Entity entity, Type type)
        {
            if (_disposed) return;
            var (handlers, isGeneric) = GetHandlersAndGenericFlag(type);
            if (isGeneric || handlers == null || handlers.IsEmpty) return;
            if (_adapter.GetComponent(entity, type) is not { } comp) return;
            UpdateCache(entity, type, comp);
            InvokeHandlers(entity, type, comp, null, true);
        }

        void OnComponentRemovedNonGeneric(Entity entity, Type type)
        {
            if (_disposed) return;
            var (handlers, isGeneric) = GetHandlersAndGenericFlag(type);
            if (isGeneric || handlers == null || handlers.IsEmpty) return;
            object? old = TryGetFromCache(entity, type);
            if (old == null) return;
            RemoveFromCache(entity, type);
            InvokeHandlers(entity, type, old, null, false);
        }

        void OnComponentUpdatedNonGeneric(Entity entity, Type type, object? oldComponent, object? newComponent)
        {
            if (_disposed || newComponent == null) return;
            var (handlers, isGeneric) = GetHandlersAndGenericFlag(type);
            if (isGeneric || handlers == null || handlers.IsEmpty) return;
            UpdateCache(entity, type, newComponent);
            InvokeHandlers(entity, type, newComponent, oldComponent ?? TryGetFromCache(entity, type), true);
        }

        (ImmutableList<HandlerEntry>? handlers, bool isGeneric) GetHandlersAndGenericFlag(Type type)
        {
            using (Lock.Read(_lock))
                return (_handlers.TryGetValue(type, out var l) ? l : null, _genericSubscribedTypes.Contains(type));
        }

        void InvokeHandlers(Entity entity, Type type, object component, object? oldComponent, bool isAddOrUpdate)
        {
            var list = TryGetHandlers(type);
            if (list == null) return;
            var del = GetCachedDelegates(type);
            foreach (var e in list)
            {
                Interlocked.Increment(ref _executingCount);
                _executor.Execute(() =>
                {
                    try
                    {
                        if (isAddOrUpdate)
                            if (oldComponent == null) del.Added(e.Handler, entity, component);
                            else del.Changed(e.Handler, entity, component, oldComponent);
                        else del.Removed(e.Handler, entity, component);
                    }
                    finally { Interlocked.Decrement(ref _executingCount); }
                }, e.Mode);
            }
        }

        ComponentTypeCache<T> GetCacheForType<T>() where T : struct => (ComponentTypeCache<T>)_componentCaches.GetOrAdd(typeof(T), _ => new ComponentTypeCache<T>());
        IComponentTypeCache CreateCache(Type type) => (IComponentTypeCache)Activator.CreateInstance(typeof(ComponentTypeCache<>).MakeGenericType(type))!;
        void UpdateCache(Entity entity, Type type, object component) => _componentCaches.GetOrAdd(type, CreateCache).Set(entity, component);
        object? TryGetFromCache(Entity entity, Type type) => _componentCaches.TryGetValue(type, out var c) ? c.Get(entity) : null;
        void RemoveFromCache(Entity entity, Type type) { if (_componentCaches.TryGetValue(type, out var c)) c.Remove(entity); }

        ImmutableList<HandlerEntry>? TryGetHandlers(Type type)
        {
            if (_disposed) return null;
            using (Lock.Read(_lock))
                return _handlers.TryGetValue(type, out var l) ? l : null;
        }

        MethodDelegates GetCachedDelegates(Type type) => _handlerMethodDelegates.GetOrAdd(type, t =>
        {
            var closed = typeof(IComponentLifecycleHandler<>).MakeGenericType(t);
            var onAdded = closed.GetMethod("OnAdded", BindingFlags.Public | BindingFlags.Instance)!;
            var onRemoved = closed.GetMethod("OnRemoved", BindingFlags.Public | BindingFlags.Instance)!;
            var onChanged = closed.GetMethod("OnChanged", BindingFlags.Public | BindingFlags.Instance)!;
            static TDelegate Create<TDelegate>(MethodInfo method) where TDelegate : Delegate
            {
                var p = method.GetParameters();
                var h = Expression.Parameter(typeof(object), "h");
                var e = Expression.Parameter(typeof(Entity), "e");
                var args = new List<Expression> { e };
                var rest = new List<ParameterExpression>();
                for (int i = 1; i < p.Length; i++)
                {
                    var param = Expression.Parameter(typeof(object), $"a{i}");
                    rest.Add(param);
                    args.Add(Expression.Convert(param, p[i].ParameterType));
                }
                return Expression.Lambda<TDelegate>(Expression.Call(Expression.Convert(h, method.DeclaringType!), method, args), new[] { h, e }.Concat(rest)).Compile();
            }
            return new(Create<Action<object, Entity, object>>(onAdded), Create<Action<object, Entity, object>>(onRemoved), Create<Action<object, Entity, object, object>>(onChanged));
        });

        void CheckDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(ComponentLifecycleManager)); }

        void UnsubscribeGenericEvents(IGenericComponentEventSource generic)
        {
            foreach (var kv in _genericSubscriptionInfos)
                try
                {
                    typeof(IGenericComponentEventSource).GetMethod("UnsubscribeAdded")!.MakeGenericMethod(kv.Key).Invoke(generic, new[] { kv.Value.Added });
                    typeof(IGenericComponentEventSource).GetMethod("UnsubscribeRemoved")!.MakeGenericMethod(kv.Key).Invoke(generic, new[] { kv.Value.Removed });
                    typeof(IGenericComponentEventSource).GetMethod("UnsubscribeUpdated")!.MakeGenericMethod(kv.Key).Invoke(generic, new[] { kv.Value.Updated });
                }
                catch { }
            _genericSubscriptionInfos.Clear(); _genericSubscribedTypes.Clear();
        }

        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
            using (Lock.Write(_lock))
            {
                if (_subscribed)
                {
                    _adapter.ComponentAdded -= OnComponentAddedNonGeneric;
                    _adapter.ComponentRemoved -= OnComponentRemovedNonGeneric;
                    _adapter.ComponentUpdated -= OnComponentUpdatedNonGeneric;
                    _subscribed = false;
                }
                if (_adapter.AsGeneric() is { } generic) UnsubscribeGenericEvents(generic);
            }
            while (Interlocked.CompareExchange(ref _executingCount, 0, 0) > 0) Thread.Sleep(1);
            var disposables = new List<IDisposable>();
            using (Lock.Write(_lock))
            {
                if (_disposeHandlersOnShutdown)
                    foreach (var list in _handlers.Values)
                        foreach (var e in list)
                            if (e.Handler is IDisposable d) disposables.Add(d);
                foreach (var c in _componentCaches.Values) if (c is IDisposable d) disposables.Add(d);
                _handlers.Clear(); _componentCaches.Clear(); _handlerMethodDelegates.Clear();
            }
            foreach (var d in disposables) try { d.Dispose(); } catch { }
            _lock.Dispose();
        }
    }
}
相关推荐
2501_941149502 小时前
2026 级微服务演进:深度解析 Cosvice 架构下的服务编排与性能调优
微服务·云原生·架构
曜垣2 小时前
用 Golang + AI 做了一个四柱排盘分析 Demo:从排盘算法到 SSE 流式输出全记录
架构
数据中穿行2 小时前
液体火箭发动机简化数字仿真系统
架构
毛骗导演2 小时前
发送一句「你好」,为什么花掉了几千个 Token?——深读 OpenClaw 的 Context 注入机制
前端·架构
bugcome_com2 小时前
C# 多线程实战指南:从线程创建到管理与终止
c#
爱看科技2 小时前
微美全息(NASDAQ:WIMI)协同量子生成网络架构:为量子生成对抗学习铺就优化通途
网络·学习·架构
我是唐青枫2 小时前
C#.NET 源生成器 深入解析:编译时代码生成与增量生成器实战
c#·.net
人工智能AI技术2 小时前
GTC 2026首日:C#对接NVIDIA物理AI,工业仿真开发全流程
人工智能·c#
嚴寒2 小时前
云服务核心组件:OSS 与 RDS 全面指南(2026版)
数据库·后端·架构