首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

弱引用集合的陷阱:Count 不可靠、Foreach 失效、正確封裝模式

     編輯:關於C#

問題場景

使用 WeakReference 做緩存時,將其放入 List<WeakReference> 或 Dictionary,會遇到兩大核心問題:

問題一:Count 屬性不可靠

集合中有 10 個 WeakReference,但 GC 可能在讀取 Count 期間回收部分目標物件。Count 返回的是**容器槽位數**,非**存活物件數**。甚至讀取過程中 GC 介入導致並發不一致。

問題二:Foreach 迭代器失效

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?

即使實作 Count => _list.Count(wr => wr.TryGetTarget(out _)),返回值在使用瞬間可能已過期(GC 介入)。與其給予錯誤安全感,不如**不提供**,強制呼叫端處理「可能為 null」的現實。

更好的替代:ConditionalWeakTable / WeakDictionary

  • ConditionalWeakTable<TKey, TValue>:.NET 內建,鍵為弱引用,值隨鍵存活,支援 GetValue 原子獲取/建立,適合緩存、附加屬性。
  • WeakDictionary (社區實作):鍵值皆弱引用,定期清理。

最佳實踐原則

  1. 不要將 WeakReference 直接放入標準集合 當作一般元素使用。
  2. 封裝專用集合類別,隱藏 Count/GetEnumerator,提供語意明確的 API (TryGet, Cleanup)。
  3. 使用短弱引用 (new WeakReference(obj)),避免長弱引用延遲 Finalizer 執行。
  4. 併發環境加鎖,雙重檢查模式 (如 Singleton) 同理。

結語

弱引用是性能優化的利器,但放入集合瞬間變身「定時炸彈」。理解 GC 與枚舉器的交互機制,採用封裝模式隔離複雜度,才能安全享受其帶來的記憶體彈性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved