介绍您可能知道,事件处理是内存泄漏的常见来源,它是由不再使用的持久化对象产生的。您可能认为它们应该被回收,但它们并没有被回收,而且有充分的理由。在这篇简短的文章中(希望如此),我将在.Net框架中的上下文事件处理中展示这个问题,然后我将教你这个问题的标准解决方案,即弱事件模式。有两种方法,即:“传统”方法(好吧,在.Net4.5之前,所以没那么旧),实现起来比较麻烦,以及.Net4.5框架提供的新方法,它非常简单可能(源代码可在此处获得。)从常见事物入手在深入本文之前,让我们回顾一下代码中两个最常用的事物:类和方法。事件溯源让我向您介绍一个基本但有用的事件溯源类,它具有足够的复杂性来说明这一点:publicclassEventSource{publiceventEventHandlerEvent=delegate{};publicvoidRaise(){事件(this,EventArgs.Empty);对于那些对奇怪的空委托初始化方法(delegate{})感到好奇的人来说,这是一个确保事件始终被初始化的技巧,这样您就不必在每次使用它之前都调用它。检查它是否不为NULL。触发垃圾回收的实用方法在.NET中,垃圾回收以不确定的方式触发。这对我们的实验不利,因为它需要以确定性的方式跟踪对象的状态。因此,我们必须定期触发自己的垃圾收集操作,同时避免重复管道代码,该代码已在特定方法中发布:staticvoidTriggerGC(){Console.WriteLine("StartingGC.");GC.收集();GC.WaitForPendingFinalizers();GC.收集();Console.WriteLine("GCfinished.");}虽然不是很复杂,但如果你对这种模式不是很熟悉,还是有必要稍微解释一下:***GC.Collect()triggers.net'sCLRgarbagecollector。对于负责清理不再使用的对象的对象,以及类中没有终结器(即c#中的析构函数)的对象,CLR垃圾收集器足以进行GC。WaitForPendingFinalizers()等待其他对象的终结器执行;我们需要这样做,因为正如您将看到的,我们使用终结器方法来跟踪我们的对象何时被收集。第二个GC.Collect()确保新生成的对象也被清理引入问题首先让我们尝试借助一些理论和最重要的演示来了解事件侦听器的问题。背景一个对象要作为事件监听器,需要将它的一个实例方法注册为另一个可以产生事件的对象(即事件源)的事件处理器。事件源必须维护到事件侦听器对象的链接。引用以在事件发生时调用此侦听器的处理程序方法。这是有道理的,但如果引用是强引用,则侦听器将成为事件源的依赖项并且不能被垃圾回收,即使引用它的最后一个对象是事件源。这是下面发生的事情的详细图表:事件处理问题这不会成为问题,如果您可以控制侦听器对象的生命周期,则可以在不再需要侦听器时取消订阅事件源,通常您可以使用一次性模式(即用即扔模式)。但是如果你不能在监听器生命周期内验证单点响应,你就不能确定性地处理它,你必须依赖GC处理......这永远不会考虑你准备的对象,因为只要事件源还存在!示例理论都很好,但让我们看看问题和实际代码。这是我们勇敢的事件监听器,有点天真,我们很快就会明白为什么:}publicNaiveEventListener(EventSourcesource){source.Event+=OnEvent;}~NaiveEventListener(){Console.WriteLine("NaiveEventListenerfinalized.");}}用一个简单的例子来看看它是如何工作的:Console.WriteLine("===Naivelistener(bad)===");EventSourcesource=newEventSource();NaiveEventListenerlistener=newNaiveEventListener(源);来源.Raise();Console.WriteLine("Settinglistenertonull.");听众=空;触发GC();来源.Raise();Console.WriteLine("设置源为空");来源=空;触发GC();输出:EventListenerreceivedevent。设置侦听器为空。启动GC。GC完成。事件监听器接收事件。设置源为空。启动GC。NaiveEventListener完成。R艾斯()”;完美,好像我们在听。“Settinglistenertonull.”:我们将本地事件监听器对象引用设置为空值,这应该允许垃圾收集器回收。“启动GC。“:垃圾收集开始。“GCfinished.”:垃圾回收开始,但是我们的事件监听器没有被回收器回收,这证明事件监听器的析构函数没有被调用。“EventListenerreceivedevent.”:第二次调用“source.Raise()”,确认监听器还活着。事件。“StartingGC.”:第二次垃圾回收。“NaiveEventListenerfinalized.”:这次naive事件监听器终于被回收了,迟做总比不做好。“GCfinished.”:第二次垃圾收集完成。结论:确实存在隐藏的强事件监听Reference,目的是防止在事件源被回收之前被回收!希望这个问题有一个标准的解决方案:让事件源通过弱引用来引用监听器,当事件源存在时监听器对象也可以被回收……这里有一个标准模式及其在.NET框架:弱事件模式(http://msdn.microsoft.com/en-us/library/aa970850.aspx)。而在.Net框架中有一个标准模式及其实现:弱事件模式。#p#弱事件模式让我们看看如何在.NET中处理这个问题,通常有不止一种方法可以做到,但在这种情况下,直接决定:如果您使用的是.Net4.5,那么您将受益于更简单的实现。另外,您将不得不依靠一些人为的手段。传统方式。WeakEventManager是所有模式管道的包装器。IWeakEventListener是一个管道,它允许一个组件连接到WeakEventManager小部件(这些位于WindowBase程序集中,如果您不是开发WPF项目,则需要引用您自己的,您应该准确地引用WindowBase)所以这是一个两步过程。首先通过子类化WeakEventManager实现一个自定义事件管理器:重写StartListening和StopListening方法分别注册一个新的处理程序和取消注册一个现有的处理程序;它们将被WeakEventManager基类使用。为自定义事件管理器的用户提供两种访问侦听器列表的方法,名为“AddListener”和“RemoveListener”。提供一种通过在自定义事件管理器上公开静态属性来获取当前线程的事件管理器的方法。然后让监听器实现IWeakEventListenr接口:实现ReceiveWeakEvent方法来尝试处理这个事件。如果事件被正确处理,它将返回true。说的很多,但是相对来说可以转换成一些代码:首先,自定义的弱事件管理器:if(manager==null){manager=newEventManager();SetCurrentManager(typeof(EventManager),manageridana}mAdder;(EventSourcesource,IWeakEventListenerlistener){CurrentManager.ProtectedAddListener(source,listener);}publicstaticvoidRemoveListener(EventSourcesource,IWeakEventListenerlistener){CurrentManager.ProtectedRemoveListener(source,listener);}protectedoverridevoidStartListening(objectsource){((EventSource)source).Event+=DeliverEvent;}protectedoverridevoidStopListening(objectsource){((EventSource)source).Event-=DeliverEvent;}}其次是事件监听器:publicclassLegacyWeakEventListener:IWeakEventListener{privatevoidOnEvent(objectsource,EventArgsargs){Console.WriteLine("LegacyWeakEventListenerreceivedevent.");}publicLegacyWeakEventListener(EventSourcesource){EventManager.AddListener(source,this);}publicboolReceiveWeakEvent(TypemanagerType,objectsender,EventArgse){OnEvent(sender,e);返回真;}~LegacyWeakEventListener(){Console.WriteLine("LegacyWeakEventListenerfinalized.");}}检查下:Console.WriteLine("===Legacyweaklistener(better)===");EventSourcesource=newEventSource();LegacyWeakEventListenerlistener=newLegacyWeakEventListener(源);来源.Raise();Console.WriteLine("Settinglistenertonull.");听众=空;触发GC();来源.Raise();Console.WriteLine("设置源为空");来源=空;TriggerGC();输出:LegacyWeakEventListenerreceivedevent。设置侦听器为空。启动GC。LegacyWeakEventListener已完成。GC完成。设置源为空。启动GC。GCfinished.非常好的,它起作用了,我们的事件侦听器对象现在可以在第一次GC中被正确销毁,即使事件源对象仍然存在,并且不再泄漏内存。但是编写一个简单的侦听器需要大量代码,想象一下你有一堆这样的侦听器,你必须为每种类型编写一个弱事件管理器!如果你擅长代码重构,你可以找到一个聪明的方法来重构所有常见的代码。在.Net4.5之前,你必须自己实现弱事件管理器,但是现在,.Net提供了解决这个问题的标准方案,让我们现在回顾一下!.Net4.5方式.Net4.5引入了遗留WeakEventManager的新通用版本:WeakEventManager
