简体   繁体   English

事件处理程序的匿名方法不是泄漏?

[英]Anonymous method for event handler not a leak?

Whenever you add a delegate to an event handler, you should remove it later, right? 每当您向事件处理程序添加委托时,您应该稍后将其删除,对吧? So if you attach an anonymous method to an event, does this create an event handler leak since you can't later remove it? 因此,如果您将匿名方法附加到事件,这是否会创建事件处理程序泄漏,因为您以后无法将其删除? This code sample from http://msdn.microsoft.com/en-us/library/0yw3tz5k%28VS.80%29.aspx seems to imply that this an okay practice though. 来自http://msdn.microsoft.com/en-us/library/0yw3tz5k%28VS.80%29.aspx的代码示例似乎暗示这是一个好的做法。

// Create a handler for a click event
button1.Click += delegate(System.Object o, System.EventArgs e)
                   { System.Windows.Forms.MessageBox.Show("Click!"); };

Is this really an okay practice? 这真的是一个好的做法吗?

Whenever you add a delegate to an event handler, you should remove it later, right? 每当您向事件处理程序添加委托时,您应该稍后将其删除,对吧?

Not necessarily, no. 不一定,没有。 Often you want the event handler to stay valid for as long as the event itself can be raised - that's certainly very common with UIs. 通常,您希望事件处理程序在事件本身可以被引发时保持有效 - 这在UI中肯定非常常见。

Is this really an okay practice? 这真的是一个好的做法吗?

Absolutely, so long as you don't need to unhook the handler. 当然,只要你不需要解开处理程序。 Think about the point at which you'd unhook the event handler. 想想你解开事件处理程序的那一点。 If it's "when the form (or button, or whatever) is elegible for garbage collection" then what benefit is there in removing the handler? 如果它是“当表单(或按钮,或其他什么)可以用于垃圾收集时”那么删除处理程序有什么好处? Just let it be garbage collected with the form... 只是让它与表单垃圾收集......

Whenever you add a delegate to an event handler, you should remove it later, right? 每当您向事件处理程序添加委托时,您应该稍后将其删除,对吧?

Well, not always . 好吧,并非总是如此 There are two reasons why you'd want to remove an event handler that you're adding: 您要删除要添加的事件处理程序有两个原因:

  1. You're constantly adding handlers to the same instance that are short lived. 您经常将处理程序添加到短暂的同一实例中。 If you didn't remove them then there would be more and more handlers added, when most of them aren't needed. 如果你没有删除它们,那么当不需要大多数处理程序时,会添加越来越多的处理程序。
  2. Your handler internally holds onto a reference to an object who's lifetime is much shorter than the lifetime of whatever object the event belongs to, and the event handler won't be (or can't be) called once that other object goes out of scope. 你的处理程序内部保持对一个对象的引用,该对象的生命周期比事件所属的任何对象的生命周期短得多,并且一旦其他对象超出范围,事件处理程序将不会(或不能)被调用。 Leaving the event handler attached will either force it to stay in memory longer than desired, or possibly result in using an object that's "stale" and shouldn't be used anymore. 保持连接事件处理程序将强制它在内存中停留的时间超过预期,或者可能导致使用“陈旧”的对象并且不应再使用它。 (For example, if the resource has already been disposed you don't want that event to fire anymore.) (例如,如果资源已被处置,则您不希望再触发该事件。)

The reason #2 is an issue is because of how Garbage Collection works in C#. #2的原因是因为垃圾收集在C#中的工作原理。 It marks all objects that it can be 100% sure are in scope as "alive", and then follows everything that any of those "alive" objects reference as also being "alive" until it's followed every reference in every live object. 它将所有可以100%确定的对象标记为“活着”的范围,然后跟随任何那些“活着”对象引用的所有对象,即“活着”,直到它跟随每个活动对象中的每个引用。 Anything that was never marked as "alive" is then deemded "dead" and eligible for garbage collection. 任何从未被标记为“活着”的东西都被认为是“死”,并且有资格进行垃圾收集。

When you have an event handler attached to an event that delegate contains two things, an instance of an object and a method to run on that object. 如果事件处理程序附加到委托包含两件事的事件,则该对象的实例和在该对象上运行的方法。 That referenced object won't be able to be garbage collected until either: 在以下情况之前,引用的对象将无法进行垃圾回收:

  1. The object with the event is no longer "alive". 事件的对象不再“活着”。
  2. You remove the event handler (ie the reference to) your delegate, allowing your object to be freed earlier. 您删除了您的委托的事件处理程序(即引用),允许您的对象更早被释放。

That said, a significant percentage of cases don't apply to either of those, so there's no need to bother removing the event handlers. 也就是说,很大一部分案例不适用于其中任何一个案例,因此无需费心去除事件处理程序。

As an example, I often see people removing event handlers just before the event object goes out of scope. 作为一个例子,我经常看到人们在事件对象超出范围之前删除事件处理程序。 That's pointless. 那毫无意义。 If the object is out of scope there's no problem with it holding onto references to...whatever. 如果对象超出范围,那么保持对...的引用没有问题。

Now, if you are in one of those few situations in which you do need to unsubscribe the event handler, and you're using an anonymous method you need to...not. 现在,如果您处于需要取消订阅事件处理程序的少数情况之一,并且您正在使用匿名方法,则需要...不是。 Just create a class that can make it a named method and use that. 只需创建一个可以使其成为命名方法并使用它的类。

Subscibing to event with anonymous method (or lambda expression) potentially can lead to memory leaks, but not in this case. 使用匿名方法(或lambda表达式)subscibing到事件可能会导致内存泄漏,但在这种情况下不会。

In this case compiler will generate an anonymous method inside current class and you don't have any memory related issues as long as your button will not live much longer than your object. 在这种情况下,编译器将在当前类中生成匿名方法,并且只要您的按钮不会比您的对象长得多,您就不会遇到任何与内存相关的问题。

The use of inline anonymous delegates as event handlers does prevent those handlers being removed, as you stated. 如您所述,使用内联匿名委托作为事件处理程序可以防止删除这些处理程序。 This can indeed cause problems, especially if the delegate comes from a different object. 这确实会导致问题,特别是如果委托来自不同的对象。 If you had a class A with a public event E, and E was subscribed to by a class B (that has no containing/contains relationship to A) attaching an anonymous handler H, H is technically a member of B, and so as long as it's referred to (by being attached to A's event), B won't be GCed. 如果你有一个带有公共事件E的A类,并且E被B类订阅(它没有包含/包含与A的关系)附加一个匿名处理程序H,H在技术上是B的成员,所以只要因为它被称为(通过附加到A的事件),B将不会被GC。 B's lifetime becomes tied to A's, and if you didn't want or expect that you now have a memory leak. B的生命周期与A有关,如果你不想或期望你现在有内存泄漏。

Now, that's not always a problem. 现在,这并不总是一个问题。 If B contains A, or the other way around, then A and B's lifetimes are already tied together anyway; 如果B包含A,或者相反,那么无论如何A和B的生命周期已经联系在一起了; in your case, the button itself will very probably be disposed of before its containing control that attached the handler. 在你的情况下,按钮本身很可能在其包含附加处理程序的控件之前被处理掉。 Similarly, A and B could be long-lived objects (say, the program's main form and a data repository) and won't be GCed until the program terminates anyway. 类似地,A和B可以是长期存在的对象(例如,程序的主要形式和数据存储库),并且在程序终止之前不会进行GC操作。 As long as you never have to care about a difference in lifetime between A and B, and you never need to detach H from E to stop exhibiting whatever behavior H has, then you can attach all the anonymous handlers you like. 只要您永远不必关心A和B之间的生命差异, 并且您永远不需要从E中分离H来停止展示H具有的任何行为,那么您可以附加您喜欢的所有匿名处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM