每当您向事件处理程序添加委托时,您应该稍后将其删除,对吧?因此,如果您将匿名方法附加到事件,这是否会创建事件处理程序泄漏,因为您以后无法将其删除?来自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!"); };
这真的是一个好的做法吗?
答案 0 :(得分:9)
每当您向事件处理程序添加委托时,您应该稍后将其删除,对吧?
不一定,不。通常,只要事件本身可以引发,您就希望事件处理程序保持有效 - 这在UI中肯定很常见。
这真的是一个好的做法吗?
当然,只要你不需要取消挂钩处理程序。想想你解开事件处理程序的那一点。如果它是“当表单(或按钮,或其他什么)可以用于垃圾收集时”那么删除处理程序有什么好处?只需将其与表单垃圾一起收集......
答案 1 :(得分:5)
每当您向事件处理程序添加委托时,您应该稍后将其删除,对吧?
好吧,不是总是。您有两个原因要删除要添加的事件处理程序:
#2的原因是因为垃圾收集在C#中的工作原理。它将所有可以100%确定的对象标记为“活着”的范围,然后跟随任何那些“活着”对象引用的所有对象,即“活着”,直到它跟随每个活动对象中的每个引用。任何从未被标记为“活着”的东西都被认为是“死”,并且有资格进行垃圾收集。
当一个事件处理程序附加到一个事件时,该委托包含两件事,一个对象的实例和一个在该对象上运行的方法。在以下情况之前,引用的对象将无法进行垃圾收集:
尽管如此,很大一部分案例并不适用于其中任何一个案例,因此无需费心去除事件处理程序。
作为一个例子,我经常看到人们在事件对象超出范围之前删除事件处理程序。那毫无意义。如果对象超出范围,那么保持对...的引用没有问题。
现在,如果您处于需要取消订阅事件处理程序的少数情况之一,并且您正在使用匿名方法,则需要...不是。只需创建一个可以使其成为命名方法的类并使用它。
答案 2 :(得分:2)
使用匿名方法(或lambda表达式)潜在地对子事件进行Subscibing可能会导致内存泄漏,但在这种情况下不会。
在这种情况下,编译器会在当前类中生成一个匿名方法,只要你的按钮的寿命不比你的对象长,你就没有任何与内存相关的问题。
答案 3 :(得分:2)
如您所述,使用内联匿名委托作为事件处理程序可以防止删除这些处理程序。这确实会导致问题,特别是如果委托来自不同的对象。如果你有一个带有公共事件E的A类,并且E被B类订阅(它没有包含/包含与A的关系)附加一个匿名处理程序H,H在技术上是B的成员,所以只要因为它被称为(通过附加到A的事件),B将不会被GC。 B的生命周期与A有关,如果你不想或期望你现在有内存泄漏。
现在,这并不总是一个问题。如果B包含A,或者相反,那么无论如何A和B的生命周期已经联系在一起了;在你的情况下,按钮本身很可能在其包含附加处理程序的控件之前被处理掉。类似地,A和B可以是长期存在的对象(例如,程序的主要形式和数据存储库),并且在程序终止之前不会进行GC操作。只要您永远不必关心A和B之间的生命周期差异,和,您永远不需要从E中分离H以停止展示H具有的任何行为,那么您可以附加所有匿名处理程序你喜欢。