使用 WeakReference 做緩存時,將其放入 List<WeakReference> 或 Dictionary,會遇到兩大核心問題:
集合中有 10 個 WeakReference,但 GC 可能在讀取 Count 期間回收部分目標物件。Count 返回的是**容器槽位數**,非**存活物件數**。甚至讀取過程中 GC 介入導致並發不一致。
foreach 依賴 IEnumerator,要求迭代期間集合不變。但 GC 回收會導致邏輯上「元素消失」(Target == null),觸發 InvalidOperationException。
public sealed class WeakReferenceList<T> where T : class
{
private readonly List<WeakReference<T>> _list = new List<WeakReference<T>>();
private readonly object _lock = new object();
public void Add(T item)
{
lock (_lock) _list.Add(new WeakReference<T>(item)); // 短弱引用
}
// 不對外暴露 Count、GetEnumerator()
// 索引器:嘗試取得存活物件
public T this[int index]
{
get
{
lock (_lock)
{
if (index < 0 || index >= _list.Count)
throw new IndexOutOfRangeException();
if (_list[index].TryGetTarget(out T target))
return target;
throw new InvalidOperationException("目標物件已被 GC 回收");
}
}
}
// 掃除死弱引用 (可選,定期呼叫)
public void Cleanup()
{
lock (_lock)
{
_list.RemoveAll(wr => !wr.TryGetTarget(out _));
}
}
}
即使實作 Count => _list.Count(wr => wr.TryGetTarget(out _)),返回值在使用瞬間可能已過期(GC 介入)。與其給予錯誤安全感,不如**不提供**,強制呼叫端處理「可能為 null」的現實。
ConditionalWeakTable<TKey, TValue>:.NET 內建,鍵為弱引用,值隨鍵存活,支援 GetValue 原子獲取/建立,適合緩存、附加屬性。WeakDictionary (社區實作):鍵值皆弱引用,定期清理。Count/GetEnumerator,提供語意明確的 API (TryGet, Cleanup)。new WeakReference(obj)),避免長弱引用延遲 Finalizer 執行。弱引用是性能優化的利器,但放入集合瞬間變身「定時炸彈」。理解 GC 與枚舉器的交互機制,採用封裝模式隔離複雜度,才能安全享受其帶來的記憶體彈性。