2008-08-19 12 views
19

Sagen wir die folgende Methode haben:Kann die Verwendung von Lambda als Event-Handler einen Speicherverlust verursachen?

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(); 
} 

Wenn die Klasse mit dieser Methode instanziiert wird und die PotentialMemoryLeaker Methode wird mehrmals aufgerufen, tun wir ein Speicherleck?

Gibt es eine Möglichkeit, diesen Lambda-Event-Handler zu lösen, nachdem wir MethodThatFiresAnEvent aufrufen?

+0

Wie in den Antworten zu machen unten angegeben ist, gibt es keine Möglichkeit, es zu aushängen ohne einen Verweis zu sparen. Allerdings können Sie es sich selbst abhaken lassen: http://StackOverflow.com/Questions/1747235/weak-Event-Handler-Model-for-use-with-Lambdas/1747236#1747236 – Benjol

Antwort

16

Ja, speichern Sie es in eine Variable und haken Sie es aus.

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

Und ja, wenn Sie nicht tun, werden Sie Speicherleck, wie Sie jedes Mal eine neue Delegatobjekt anschließen werden. Sie werden dies auch bemerken, weil jedes Mal, wenn Sie diese Methode aufrufen, eine steigende Anzahl von Zeilen an die Konsole übertragen wird (nicht nur eine steigende Zahl), sondern für einen Aufruf von MethodThatFiresAnEvent eine beliebige Anzahl von Elementen einmal für jede angeschlossene anonyme Methode).

0

Ja, genauso wie normale Ereignishandler Lecks verursachen können. Da das Lambda tatsächlich geändert wird:

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

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

Also im Grunde ist es nur kurze Hand für das, was wir haben in 2.0 all diesen Jahren im Einsatz.

4

Sie werden nicht nur Speicher verlieren, Sie werden auch Ihr Lambda mehrmals aufgerufen. Jeder Aufruf von 'PotentialMemoryLeaker' fügt der Ereignisliste eine weitere Kopie des Lambda hinzu, und jede Kopie wird aufgerufen, wenn 'AnEvent' ausgelöst wird.

1

Ihr Beispiel kompiliert nur zu einem Compiler namens private innere Klasse (mit Feld fireCount und eine Compiler-benannte Methode). Jeder Aufruf von PotentialMemoryLeaker erstellt eine neue Instanz der Schließklasse, in der foo einen Verweis über einen Delegaten auf die einzelne Methode behält.

Wenn Sie nicht das gesamte Objekt referenzieren, das PotentialMemoryLeaker besitzt, wird das alles als Müll gesammelt. Andernfalls können Sie entweder foo auf null oder leer foo der Event-Handler-Liste setzen dies durch Schreiben:

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

Natürlich haben Sie Zugriff auf die privaten Mitglieder MyObject Klasse brauchen würde.

3

Sie können Nun zu erweitern, was here getan wurde Delegierten sicherer zu verwenden (keine Speicherlecks)

+1

der Link ist tot – thumbmunkeys