弱参考

在 .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 建议的那样:

  • 仅在必要时使用长弱引用,因为在完成后对象的状态是不可预测的。
  • 避免对小对象使用弱引用,因为指针本身可能大或大。
  • 避免使用弱引用作为内存管理问题的自动解决方案。相反,开发一个有效的缓存策略来处理应用程序的对象。