ConditionalWeakTable 是什么,适合解决什么问题
ConditionalWeakTable
是 .NET 提供的一个线程安全、弱引用的键值映射结构,核心用途是「把额外数据临时挂载到某个对象实例上」,且不阻止该对象被 GC 回收。它不是通用字典,不能替代 Dictionary<object t></object>
;它的设计目标很明确:避免内存泄漏,同时支持在不修改原类型的前提下扩展对象行为(比如 AOP、诊断、序列化上下文等场景)。
常见错误现象包括:用普通字典存
object → metadata
导致目标对象无法释放;或用 WeakReference
手动管理又容易出现竞态或空引用。
使用场景典型如:
给第三方类的实例附加调试 ID 或调用栈快照
在序列化器中为每个正在序列化的对象缓存临时状态
实现类似 WPF 的依赖属性附加逻辑(但更轻量)
如何正确添加和获取附加数据
关键在于理解它的泛型参数:
ConditionalWeakTable<tkey tvalue></tkey>
中的 TKey
必须是引用类型,且内部按对象标识(reference equality)匹配,不是值相等。
添加数据只需调用
Add
或更安全的 GetValue
(自动初始化):
private static readonly ConditionalWeakTable<object, string> _debugTags
= new();
<p>// 推荐方式:用 GetValue 避免重复创建
string tag = <em>debugTags.GetValue(someObj, key => $"tag</em>{Guid.NewGuid()}");</p><p>// 不推荐直接 Add:可能抛出 ArgumentException(键已存在)
// <em>debugTags.Add(someObj, $"tag</em>{DateTime.Now.Ticks}");</p>注意:
GetValue
的工厂委托只会在键首次访问时执行,后续返回缓存值
工厂函数内不要捕获外部变量并持有长生命周期引用,否则可能意外延长对象存活
TValue
本身不被弱引用保护——如果它是引用类型且被其他地方强引用,它自己不会被 GC;但只要 TKey
被回收,整个键值对就从表中移除
为什么不能用 Dictionary