2009-07-14 3 views
1

Dies ist ein WinForm MDI-Anwendungsproblem (.NET Framework 3.0). Es wird in C# beschrieben. Tut mir leid, es ist ein bisschen lang, weil ich versuche, die Dinge so klar wie möglich zu machen.MDI WinForm-Anwendung und doppelte Kind Formularspeicherverlust

Ich habe eine MDI-Anwendung. Irgendwann finde ich, dass ein MDI-Kind-Formular nie veröffentlicht wird. Es gibt ein Menü, das das untergeordnete MDI-Formular erstellt und es anzeigt. Wenn das untergeordnete MDI-Formular geschlossen ist, soll es zerstört werden und der von ihm übernommene Speicher sollte an .net zurückgegeben werden. Aber zu meiner Überraschung ist das nicht wahr. Alle untergeordneten MDI-Formularinstanzen werden im Speicher gehalten. Dies ist offensichtlich ein "Speicherleck". Nun, es ist kein echtes Leck in .net. Es ist nur so, dass ich denke, dass die geschlossene Form tot sein sollte, aber irgendwie gibt es zumindest eine unbekannte Referenz von der Außenwelt, die sich immer noch mit der geschlossenen Form verbindet.

Ich habe einige Artikel im Web gelesen. Einige sagen, dass wenn das untergeordnete MDI-Formular geschlossen wird, ich alle Ereignishandler unwire sollte, andernfalls können einige Ereignishandler mein Formular am Leben erhalten. Einige sagen, dass DataBindings gesäubert werden sollte, bevor das Formular geschlossen wird, ansonsten fügen die DataBindings Referenzen zu einer globalen Hashtable hinzu und behalten so mein Formular am Leben.

Mein Formular enthält ziemlich viele Dinge. Viele Event-Handler und viele DataBindings und viele BindingSources und wenige verdächtige Steuerelemente mit Benutzerkontrolle und HelpProvider. Ich erstelle eine große Methode, die alle Event-Handler von allen relevanten Steuerelementen löscht, alle DataBindings und DataSources löscht. Der HelpProvider und die Benutzersteuerelemente werden sorgfältig sortiert.

Am Ende finde ich, dass ich DataBindings und DataSources nicht löschen muss. Ereignishandler verursachen definitiv das Problem. Und die MDI-Formularstruktur trägt ebenfalls zu etwas bei.

Während meiner Experimente finde ich, dass, wenn Sie ein untergeordnetes MDI-Formular erstellen, auch wenn Sie es schließen, wird immer noch eine Instanz im Speicher sein. Die Referenz stammt aus dem PropertyStore des Hauptformulars. Dies bedeutet, dass, solange das Hauptformular nicht geschlossen ist (Anwendung endet), immer eine Instanz des untergeordneten MDI-Formulars im Speicher vorhanden ist. Die gute Nachricht ist, dass es, egal wie oft Sie die Kinderform öffnen und schließen, nur eine Instanz gibt, kein großes "Leck".

Wenn es um Ereignishandler geht, werden die Dinge schwieriger. Ich muss das ansprechen, alle Event-Handler auf meinem Formular sind anonyme Event-Handler. Hier ist ein Beispiel-Code:

//On MDI child form's design code... 

Button btnSave = new Button(); 

btnSave.Click += new System.EventHandler(btnSave_Click); 

Wo btnSave_Click ist auch ein Verfahren, in untergeordnetem MDI-Formular. Obiges gilt für verschiedene Steuerelemente und verschiedene Arten von Ereignissen. Für mich ist dies eine bidirektionale Zirkularreferenz. btnSave behält eine Referenz des untergeordneten MDI-Formulars über den Ereignishandler bei. Das untergeordnete MDI-Formular enthält eine Referenz der btnSave-Instanz. Eine solche bidirektionale Zirkularreferenz sollte für den Garbage Collector von .net kein Problem darstellen. Das bedeutet, dass ich nicht explizit das Ereignis kabellos müssen, wenn die Form angeordnet wird:

btnSave.Click -= btnSave_Click; 

Aber die Wahrheit ist nicht so. Für einige Ereignishandler sind sie sicher. Ignorieren sie keine doppelte Instanz. Bei einigen anderen Ereignisbehandlungsroutinen bewirken sie, dass eine Instanz im Speicher verbleibt (ähnlicher Effekt wie die MDI-Formularstruktur, diesmal jedoch durch die hängenden Ereignisbehandlungsroutinen). Für einige andere Event-Handler veranlassen sie, dass jede Instanz im Speicher geöffnet wird. Ich bin total verwirrt über die Unterschiede zwischen diesen drei Arten von Event-Handlern. Die Steuerelemente werden auf dieselbe Weise erstellt und das Ereignis wird auf die gleiche Weise angefügt. Was ist der Unterschied? (Erzähl mir nicht, dass es die Event-Handle-Methoden sind, die einen Unterschied machen.) Jemand hat Erfahrung mit diesem verkabelten Szenario und hat eine Antwort für mich? Danke vielmals.

Jetzt, aus Sicherheitsgründen, muss ich alle Event-Handler unwire, wenn das Formular entsorgt wird. Das wird eine lange Liste von ähnlichem Code für jedes Steuerelement sein. Gibt es eine allgemeine Möglichkeit, Ereignisse auf rekursive Weise mithilfe von Reflektion aus Steuerelementen zu entfernen? Was ist mit Leistungsproblemen?

Das ist das Ende meiner Geschichte und ich bin immer noch in der Mitte meines Problems. Für jede Hilfe, ich danke Ihnen.

Antwort

1

Ihr Problem kann durch die angebotene Lösung here gelöst werden.

0

Solange das Objekt, mit dem der Ereignishandler verdrahtet ist, im untergeordneten Formular deklariert ist, müssen Sie den Ereignishandler nicht entfernen, wenn das untergeordnete Formular entfernt wird, sondern wenn das Objekt, mit dem der Ereignishandler verbunden ist Wird außerhalb des untergeordneten Formulars deklariert, müssen Sie den Ereignishandler entfernen, wenn das untergeordnete Formular entfernt wird.

Wenn dieser Code wird mehrmals ausgeführt (und btnSave ist ein Objekt nicht in dem Kind Form deklariert)

btnSave.Click += btnSave_Click;

Dieser Code die gleiche Anzahl von Zeiten

btnSave.Click -= btnSave_Click;
ausgeführt werden muss


Möglicherweise verweisen Sie das untergeordnete Formular irgendwo in der Anwendung, dieser Verweis muss entfernt werden, bevor der Garbage Collector das untergeordnete Formularobjekt entfernt.