假设我们有以下方法:
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
?
是的,它保存到一个变量并将其取下。
DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); };
foo.AnEvent += evt;
foo.MethodThatFiresAnEvent();
foo.AnEvent -= evt;
是的,如果你不这样做,你会泄漏内存,因为你每次都挂钩一个新的委托对象。 您还会注意到这一点,因为每次调用此方法时,它会转储到控制台线越来越多的(不只是越来越多,但对于一个呼叫MethodThatFiresAnEvent它会转储任何数量的项目,一旦每个挂钩匿名方法)。
你不会只是泄漏内存,你也将获得您的拉姆达多次调用。 “PotentialMemoryLeaker”的每个调用将拉姆达的另一个副本添加到事件列表中,当“AnEvent”被激发每一个副本将被命名。
那么你可以扩展做了什么在这里 ,使与会代表使用更安全(没有内存泄漏)
你举的例子只是编译成一个编译器命名的私有内部类(与现场firedCount和编译器命名的方法)。 到PotentialMemoryLeaker每次调用创建closure类到其中foo保持由委托单方法来参考的新实例。
如果不引用拥有PotentialMemoryLeaker整个对象,然后将全部在垃圾收集。 否则,您可以设置FOO为空或通过写这个空Foo的事件处理程序列表:
foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler;
当然,你需要访问的MyObject类的私有成员。
是在相同的方式,正常的事件处理程序可能造成泄漏。 因为拉姆达实际上更改为:
someobject.SomeEvent += () => ...;
someobject.SomeEvent += delegate () {
...
};
// unhook
Action del = () => ...;
someobject.SomeEvent += del;
someobject.SomeEvent -= del;
所以基本上它只是短手,我们一直使用2.0这些年来什么。