当前位置: 首页 > 编程语言 > C#

使用匿名委托进行事件处理时的垃圾收集Share

时间:2023-04-10 21:40:07 C#

使用匿名委托进行事件处理时的垃圾收集UPDATE我已经将这里的各种答案组合成一个新问题的“OK”答案。原始问题在我的代码中,我有一个贯穿应用程序生命周期的事件发布者(此处简化为基本要素):publicclassPublisher{//ValueEventArgs继承自EventArgs由于这个发布者可以在任何地方使用,我非常满足于自己创建这个小助手类以避免重写所有订阅者中的处理代码:EnabledChanged+=(s,e)=>subscriber.Enabled=e.Value;}//(非lambda版本,如果您对lambda不满意)publicstaticvoidLink(Publisherpublisher,Controlsubscriber){publisher.EnabledChanged+=delegate(objectsender,ValueEventArgse){subscriber.Enabled=e.价值;};它工作得很好,直到我们开始在较小的机器上使用它,当我偶尔开始时:System.ComponentModel.Win32Exception没有足够的存储空间来处理这个命令事实证明,在代码中有一个地方订阅者控制表单是动态创建、添加和删除的。鉴于我对垃圾收集等的深刻理解(即没有,直到昨天),我从来没有想过要清理我身后的东西,因为在绝大多数情况下,订阅者也在应用程序的生命周期内存在。我一直在摆弄DustinCampbell的WeakEventHandler有一段时间了,但它不适用于匿名代表(反正对我来说不适用)。无论如何有这个问题吗?我真的很想避免在整个商店复制粘贴样板代码。(哦,不要再问我为什么我们一直在制作和销毁控件,这不是我的设计决定......)(PS:这是一个winforms应用程序,但我们已经升级到VS2008和.Net3.5,我应该考虑使用Wea??k事件模式?)(PPS:Rory的回答很好,但如果有人能得到WeakEventHandler的等价物以避免我不得不记住显式的UnLink/Dispose...)编辑我必须承认我通过“解决了这个问题”回收”的违规控制。但是,由于我使用的“密钥”显然不是唯一的(呜咽),因此解决方法再次困扰着我。我刚刚在这里找到了其他链接(试过这个-看起来有点太弱了-即使目标还活着,GC也会清除委托,与s相同的问题,o????在下面回答),在这里(强制你修改publisher,并且“没有真正起作用”与匿名代表”和这里(DustinCampbell引述-不完整)。在我看来,我正在寻找的东西可能在语义上是不可能的-闭包旨在“即使在我离开时我也会闲逛”。我找到了另一种解决方法,所以我会坚持下去,等待众神的声音。我知道这个问题很老了,但是地狱-我找到了它,我认为其他人也可能这样做。我正在努力解决相关问题并可能有一些见解。你提到了DustinCampbell的WeakEventHandler-它真的不能通过设计使用匿名方法。当我意识到a)99%的时候我需要这样的东西,他原来的解决方案会更安全,b)在少数情况下我需要它(注意:有)而不是“因为lambda是如此美丽和简洁”而不是“如果你更聪明一点你可以让它工作。您的示例看起来像是一次性案例,有点棘手可能会导致一个相当简洁的解决方案。publicstaticclassLinker{publicstaticvoidLink(Publisherpublisher,Controlsubscriber){//匿名方法仅通过弱引用引用订阅者,因此它的存在不会干扰垃圾收集varsubscriber_weak_ref=newWeakReference(subscriber);//只要匿名方法持有对它的引用,这个实例变量就会保留在内存中//我们声明并初始化它以//保留内存(另外,编译器会抱怨未初始化的//变量)EventHandler>handler=null;//创建处理程序时,它将获取对内部使用的局部//变量的引用,在函数作用域结束后将它们保存在内存中//处理程序=委托(对象发送者,ValueEventArgse){varsubscriber_strong_ref=subscriber_weak_ref.TargetasControl;如果(subscriber_strong_ref!=null)subscriber_strong_ref.Enabled=e.Value;else{//从自身内部取消订阅委托是有风险的y,但是//因为只有一个实例存在并且没有其他人引用//我们可以这样做((Publisher)sender).EnabledChanged-=handler;//通过将原始实例变量指针分配给null//我们确保没有其他任何东西引用匿名方法//并且它可以被收集。在此之后,弱//引用和处理程序指针本身也将符合//收集的条件。处理程序=空;}};=处理程序;传闻WPF弱事件模式会带来大量开销,因此我不会在这种特殊情况下使用它此外,在WinForm应用程序中引用核心WPF库似乎也有点繁重。如果保留对匿名委托的引用,然后在从窗体中删除控件时将其删除,则该控件应允许对控件和匿名委托进行垃圾回收。所以像这样:publicstaticclassLinker{//(非lambda版本,我对lambda不满意:)publicstaticEventHandler>Link(Publisherpublisher,Controlsubscriber){EventHandler>handler=delegate(objectsender,ValueEventArgse){subscriber.Enabled=e.Value;};publisher.EnabledChanged+=处理程序;返回处理程序;}publicstaticvoidUnLink(Publisherpublisher,EventHandler>handler){publisher.EnabledChanged-=handler;有关示例,请参阅C#中的取消订阅匿名方法。我最近根据WeakReference制作了一些示例代码://强类型弱引用publicclassWeakReference:WeakReferencewhereT:class{publicWeakReference(Ttarget):base(target){}publicWeakReference(Ttarget,booltrackResurrection):base(target,trackResurrection){}publicnewTTarget{get{returnbase.TargetasT;}set{base.Target=value;}}}//弱引用通用事件处理器publicclassWeakEventHandler:WeakReference>whereTEventArgs:EventArgs{publicWeakEventHandler(EventHandlertarget):base(target){}protectedvoidInvoke(objectsender,TEventArgse){if(Target!=空){目标(发件人,e);}}publicstaticimplicitoperatorEventHandler(WeakEventHandlerweakEventHandler){if(weakEventHandler!=null){if(weakEventHandler.IsAlive){returnweakEventHandler.Invoke;}}返回空值;}}//弱引用公共事件处理器publicclassWeakEventHandler:WeakReference{publicWeakEventHandler(EventHandlertarget):base(target){}protectedvoidInvoke(objectsender,EventArgse){if(Target!=null){Target(sender,e);}}publicstaticimplicitoperatorEventHandler(WeakEventHandlerweakEventHandler){if(weakEventHandler!=null){if(weakEventHandler.IsAlive){returnweakEventHandler.Invoke;}}返回空值;}}//可观察类,触发事件publicclassObservable{publicObservable(){Console.WriteLine("newObservable()");}~Observable(){Console.WriteLine("~Observable()");}公共事件EventHandlerOnChange;protectedvirtualvoidDoOnChange(){EventHandlerhandler=OnChange;if(handler!=null){Console.WriteLine("DoOnChange()");处理程序(这个,EventArgs.Empty);}}publicvoidChange(){DoOnChange();}}//观察者,事件监听器publicclassObserver{publicObserver(){Console.WriteLine("newObserver()");}~Observer(){Console.WriteLine("~Observer()");}publicvoidOnChange(objectsender,EventArgse){Console.WriteLine("->Observer.OnChange({0},{1})",sender,e);}}//示例用法和测试代码publicstaticclassProgram{staticvoidMain(){Observablesubject=newObservable();观察者watcher=newObserver();Console.WriteLine("订阅新的WeakEventHandler()n");subject.OnChange+=newWeakEventHandler(watcher.OnChange);主题。更改();Console.WriteLine("nObserver=null,GC");观察者=空;GC.Collect(0,GCCollectionMode.Forced);GC.WaitForPendingFinalizers();主题。更改();if(Debugger.IsAttached){Console.Write("按任意键继续......");控制台.ReadKey(true);}}}产生以下输出:newObservable()newObserver()subscribenewWeakEventHandler()DoOnChange()->Observer.OnChange(ConsoleApplication4.Observable,System.EventArgs)Observer=null,GC~Observer()DoOnChange()~Observable()按任意键继续。..(请注意取消订阅(-=)不起作用)在Egor的回答的基础上进一步构建,我认为尝试构建一个我不需要提前决定要附加哪个事件的版本我只是设法让它与通用事件处理程序一起工作:对于“标准”事件处理程序(例如FormClosingEventHandler),这有点棘手,因为你不能在T:delegate中有类型约束(除非你的名字以小马结尾)。privatestaticvoidSetAnyGenericHandler(Action>add,//将事件侦听器添加到发布者Action>remove,//从发布者S订阅者中删除事件侦听器,//ref到订阅者(传递给消费)Actionconsume)//调用时引发事件*其中T:EventArgs其中S:类{varsubscriber_weak_ref=newWeakReference(subscriber);事件处理程序处理程序=null;handler=delegate(objectsender,Te){varsubscriber_strong_ref=subscriber_weak_ref.TargetasS;!=null){Console.WriteLine("订阅者收到新事件");消费(subscriber_strong_ref,e);}else{删除(处理程序);处理程序=空;}};添加(处理程序);}(*我在这里尝试使用EventHandlerconsume,但是调用代码变得很难看,因为你必须在consumelambda中将s转换为Subscriber。)调用代码示例,取自上述示例:SetAnyGenericHandler(h=>publisher.EnabledChanged+=h,h=>publisher.EnabledChanged-=h,subscriber,(Subscribers,ValueEventArgse)=>s.Enabled=e。价值);或者,如果您更喜欢SetAnyGenericHandler>(h=>publisher.EnabledChanged+=h,h=>publisher.EnabledChanged-=h,subscriber,(s,e)=>s.Enabled=e.Value);可以将事件作为参数传递会很好,但是您不能从事件中添加/删除任何内容,就像您不能从属性中访问get/set一样(我认为不做讨厌的反射)。以上就是C#学习教程:使用匿名委托进行事件处理时的垃圾收集的全部内容,如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: