弱參考

在 .NET 中,當沒有任何引用時,GC 會分配物件。因此,雖然仍然可以從程式碼到達物件(有強烈的引用),但 GC 不會分配此物件。如果有很多大型物件,這可能會成為一個問題。

弱引用是一個引用,它允許 GC 在仍允許訪問物件的同時收集物件。弱引用僅在不存在強引用時收集物件之前的不確定時間內有效。當你使用弱引用時,應用程式仍然可以獲得對該物件的強引用,從而阻止其被收集。因此,弱引用對於保留初始化成本高昂的大物件非常有用,但如果它們沒有被主動使用,則應該可用於垃圾回收。

簡單用法:

WeakReference reference = new WeakReference(new object(), false);

GC.Collect();

object target = reference.Target;
if (target != null)
  DoSomething(target);

因此,弱引用可用於維護物件的快取。但是,重要的是要記住,在重新建立強引用之前,垃圾收集器始終存在到達物件的風險。

弱引用也可以方便地避免記憶體洩漏。典型的用例是事件。

假設我們對源上的事件有一些處理程式:

Source.Event += new EventHandler(Handler)

此程式碼註冊一個事件處理程式,並建立一個從事件源到偵聽物件的強引用。如果源物件的生命週期比偵聽器長,並且當沒有其他引用時,偵聽器不再需要該事件,則使用普通的 .NET 事件會導致記憶體洩漏:源物件將偵聽器物件儲存在記憶體中應該是垃圾收集。

在這種情況下,使用弱事件模式可能是個好主意。

就像是:

public static class WeakEventManager
    {
    public static void SetHandler<S, TArgs>(
    Action<EventHandler<TArgs>> add,
    Action<EventHandler<TArgs>> remove,
    S subscriber,
    Action<S, TArgs> action)
    where TArgs : EventArgs
    where S : class
        {
            var subscrWeakRef = new WeakReference(subscriber);
            EventHandler<TArgs> handler = null;

            handler = (s, e) =>
            {
                var subscrStrongRef = subscrWeakRef.Target as S;
                if (subscrStrongRef != null)
                {
                    action(subscrStrongRef, e);
                }
                else
                {
                    remove(handler);
                    handler = null;
                }
            };

            add(handler);
        }
    }

並像這樣使用:

 EventSource s = new EventSource();
 Subscriber subscriber = new Subscriber();
 WeakEventManager.SetHandler<Subscriber, SomeEventArgs>(a => s.Event += a, r => s.Event -= r, subscriber, (s,e) => { s.HandleEvent(e); });

在這種情況下,我們當然有一些限制 - 事件必須是 a

public event EventHandler<SomeEventArgs> Event;

正如 MSDN 建議的那樣:

  • 僅在必要時使用長弱引用,因為在完成後物件的狀態是不可預測的。
  • 避免對小物件使用弱引用,因為指標本身可能大或大。
  • 避免使用弱引用作為記憶體管理問題的自動解決方案。相反,開發一個有效的快取策略來處理應用程式的物件。