namespace GFramework.Cqrs.Internal; /// /// 提供基于弱键语义的线程安全缓存。 /// 该缓存用于跨容器复用与 绑定的派生元数据, /// 同时避免静态强引用阻止 collectible 程序集或热重载类型被卸载。 /// /// 缓存键类型。 /// 缓存值类型。 /// /// 该缓存只保证“命中时复用”,不保证“永久保留”。 /// 当键对象被 GC 回收后,条目会自然失效,后续访问会重新计算对应值。 /// 这是 CQRS 运行时在卸载安全与热路径性能之间的显式权衡。 /// internal sealed class WeakKeyCache where TKey : class where TValue : class { private readonly object _gate = new(); private ConditionalWeakTable _entries = new(); /// /// 获取指定键对应的缓存值;若当前未命中,则在锁保护下创建并写入。 /// /// 缓存键。 /// 创建缓存值的工厂方法。 /// 已存在或新创建的缓存值。 /// /// 返回 public TValue GetOrAdd(TKey key, Func valueFactory) { ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(valueFactory); var entries = Volatile.Read(ref _entries); if (entries.TryGetValue(key, out var cachedValue)) return cachedValue; lock (_gate) { entries = _entries; if (entries.TryGetValue(key, out cachedValue)) return cachedValue; var createdValue = valueFactory(key) ?? throw new InvalidOperationException("The value factory returned null."); entries.Add(key, createdValue); return createdValue; } } /// /// 获取指定键对应的缓存值;若当前未命中,则在锁保护下使用附加状态创建并写入。 /// /// 创建缓存值时需要携带的附加状态类型。 /// 缓存键。 /// 创建缓存值时复用的附加状态。 /// 基于键与附加状态创建缓存值的工厂方法。 /// 已存在或新创建的缓存值。 /// /// 返回 public TValue GetOrAdd(TKey key, TState state, Func valueFactory) { ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(valueFactory); var entries = Volatile.Read(ref _entries); if (entries.TryGetValue(key, out var cachedValue)) return cachedValue; lock (_gate) { entries = _entries; if (entries.TryGetValue(key, out cachedValue)) return cachedValue; var createdValue = valueFactory(key, state) ?? throw new InvalidOperationException("The value factory returned null."); entries.Add(key, createdValue); return createdValue; } } /// /// 尝试读取当前缓存中的值,而不触发新的创建逻辑。 /// /// 缓存键。 /// 命中时返回的缓存值。 /// 若命中当前缓存则为 ;否则为 /// public bool TryGetValue(TKey key, out TValue? value) { ArgumentNullException.ThrowIfNull(key); return Volatile.Read(ref _entries).TryGetValue(key, out value); } /// /// 清空当前缓存实例。 /// /// /// 该方法主要服务于测试,便于在同一进程内隔离不同用例的静态缓存状态。 /// public void Clear() { lock (_gate) { _entries = new ConditionalWeakTable(); } } /// /// 返回指定键当前命中的缓存对象;若未命中则返回 。 /// /// 缓存键。 /// 当前缓存对象,或 /// /// 该入口仅用于测试通过反射观察缓存状态,不应用于运行时代码路径。 /// public TValue? GetValueOrDefaultForTesting(TKey key) { return TryGetValue(key, out var value) ? value : null; } }