2008-08-19 53 views
19

假设我们有以下方法:可以使用lambda作为事件处理程序导致内存泄漏?

private MyObject foo = new MyObject(); 

// and later in the class 

public void PotentialMemoryLeaker(){ 
    int firedCount = 0; 
    foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);}; 
    foo.MethodThatFiresAnEvent(); 
} 

如果用这种方法的类实例化和PotentialMemoryLeaker方法被调用多次,我们是否泄漏内存?

在我们完成调用MethodThatFiresAnEvent之后,有什么办法可以解开这个lambda事件处理程序吗?

+0

正如下面的回答显示,有没有办法解开它没有保存的参考。然而,你可以让它解开本身:http://stackoverflow.com/questions/1747235/weak-event-handler-model-for-use-with-lambdas/1747236#1747236 – Benjol 2009-11-17 07:57:15

回答

16

是的,将它保存到一个变量并解除它。

DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); }; 
foo.AnEvent += evt; 
foo.MethodThatFiresAnEvent(); 
foo.AnEvent -= evt; 

是的,如果你不这样做,你会泄漏记忆,你每次勾了一个新的委托对象。你也会注意到这一点,因为每次你调用这个方法时,它会向控制台转储越来越多的行(不仅仅是一个越来越多的行数,而是一次调用MethodThatFiresAnEvent它会转储任何数量的项目,一次每个连接匿名方法)。

0

是的,正常的事件处理程序可能导致泄漏。因为拉姆达实际上更改为:

someobject.SomeEvent +=() => ...; 
someobject.SomeEvent += delegate() { 
    ... 
}; 

// unhook 
Action del =() => ...; 
someobject.SomeEvent += del; 
someobject.SomeEvent -= del; 

所以基本上它只是短手,我们一直使用2.0这些年来什么。

4

你不会泄漏内存,你也会得到你的lambda多次调用。 'PotentialMemoryLeaker'的每次调用都会将lambda的另一个副本添加到事件列表中,并且当'AnEvent'被触发时将调用每个副本。

1

你的例子只是编译到一个编译器命名的私有内部类(带有字段firedCount和一个编译器命名的方法)。每次调用PotentialMemoryLeaker都会创建一个闭包类的新实例,其中foo通过委托给单个方法来保存引用。

如果您没有引用拥有PotentialMemoryLeaker的整个对象,那么这将全部被垃圾收集。否则,您可以设置为空或通过写这个空Foo的事件处理程序列表:

foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler; 

当然,你需要访问MyObject来类的私有成员。

3

那么你可以扩展做了什么here使与会代表使用更安全(没有内存泄漏)

+1

链接已经死了 – thumbmunkeys 2015-10-31 12:21:11