72 Stimmen

Müssen Sie einen Ereignishandler im Destruktor entfernen?

Ich verwende einige UserControls die in meiner Anwendung während der Laufzeit erstellt und zerstört werden (durch Erstellen und Schließen von Unterfenstern mit diesen Steuerelementen darin).
Es ist ein WPF UserControl und erbt von System.Windows.Controls.UserControl . Es gibt keine Dispose() Methode, die ich außer Kraft setzen könnte.
PPMM ist eine Singleton mit der gleichen Lebensdauer wie meine Anwendung.
Jetzt im Konstruktor meiner (WPF) UserControl füge ich einen Event-Handler hinzu:

public MyControl()
{
    InitializeComponent();

    // hook up to an event
    PPMM.FactorChanged += new ppmmEventHandler(PPMM_FactorChanged);
}

Ich habe mir angewöhnt, solche Ereignishandler im Destruktor zu entfernen:

~MyControl()
{
    // hook off of the event
    PPMM.FactorChanged -= new ppmmEventHandler(PPMM_FactorChanged);
}

Heute bin ich über diesen Artikel gestolpert und habe mich gewundert:

1) Ist dies notwendig? Oder kümmert sich der GC darum?

2) Funktioniert das überhaupt? Oder müsste ich die neu erstellte ppmmEventHandler ?

Ich bin gespannt auf Ihre Antworten.

47voto

Lasse V. Karlsen Punkte 364542

Seit PPMM ein langlebiges Objekt (Singleton) ist, macht dieser Code nicht viel Sinn.

Das Problem dabei ist, dass, solange der Ereignishandler das Objekt referenziert, er kommt nicht für die Müllabfuhr in Frage solange das andere Objekt, dem das Ereignis gehört, am Leben ist.

Daher ist es sinnlos, irgendetwas in den Destruktor zu schreiben, denn entweder:

  1. Der Ereignishandler wurde bereits entfernt, so dass das Objekt für die Garbage Collection in Frage kommt.
  2. Der Event-Handler wird nicht entfernt, das besitzende Objekt kommt nicht für die Garbage Collection in Frage, und daher wird der Finalizer nie aufgerufen
  3. Beide Objekte kommen für die Garbage Collection in Frage. In diesem Fall sollten Sie im Finalizer überhaupt nicht auf das andere Objekt zugreifen, da Sie dessen internen Zustand nicht kennen

Kurz gesagt, Tun Sie das nicht .

Nun könnte man auch anders argumentieren, wenn man einen solchen Code in die Dispose Methode, wenn Sie Folgendes implementieren IDisposable . Unter dass Fall macht es durchaus Sinn, da der Usercode, der die Dispose an einem vorher festgelegten und kontrollierten Punkt.

Der Finalizer (Destruktor) wird jedoch nur aufgerufen, wenn das Objekt für die Garbage Collection in Frage kommt und über einen Finalizer verfügt, was in diesem Fall sinnlos ist.

Was die Frage Nr. 2 betrifft, die ich als "Kann ich mich von solchen Veranstaltungen abmelden" verstehe, so können Sie dies tun. Das einzige Mal, dass Sie den Delegaten, mit dem Sie sich angemeldet haben, behalten müssen, ist, wenn Sie den Delegaten um eine anonyme Methode oder einen Lambda-Ausdruck herum konstruieren. Wenn Sie es um eine vorhandene Methode konstruieren, wird es funktionieren.


bearbeiten : WPF. Richtig, ich habe diesen Tag nicht gesehen. Sorry, der Rest meiner Antwort macht nicht viel Sinn für WPF und da ich kein WPF-Guru bin, kann ich nicht wirklich sagen. Wie auch immer, es gibt eine Möglichkeit, dies zu beheben. Es ist hier auf SO völlig legal, den Inhalt einer anderen Antwort zu klauen, wenn man ihn verbessern kann. Also, wenn jemand weiß, wie man richtig tun dies mit einem WPF-Benutzerelement, Sie sind frei, den gesamten ersten Abschnitt meiner Antwort zu heben und fügen Sie die relevanten Bits von WPF.

bearbeiten : Lassen Sie mich auch hier auf die Frage in dem Kommentar antworten.

~~

Da es sich bei der betreffenden Klasse um ein Benutzersteuerelement handelt, ist ihre Lebensdauer an ein Formular gebunden. Wenn das Formular geschlossen wird, werden alle untergeordneten Steuerelemente, die es besitzt, entsorgt, mit anderen Worten, hier ist bereits eine Dispose-Methode vorhanden .

Wenn ein Benutzersteuerelement seine eigenen Ereignisse verwaltet, ist der korrekte Weg, um dies zu handhaben, das Aushängen der Ereignishandler in der Dispose-Methode.

~~

(Rest entfernt)

11voto

Tigran Punkte 60618

Erstens würde ich sagen, verwenden Sie keine Destruktor sondern Entsorgen() um Ihre Ressourcen freizugeben.

Zweitens ist es meiner Meinung nach besser, wenn sich dieser Code in einem Objekt befindet, das sehr oft erstellt wird und eine kurze Lebensdauer hat aufpassen den Ereignishandler selbst zu entfernen, da es sich um eine Verknüpfung mit dem Halterobjekt handelt, das verhindern. die GC de sie zu sammeln .

Herzliche Grüße.

8voto

Paul Groke Punkte 5864

WPF unterstützt nicht IDisposable nun ja. Wenn Sie ein WPF-Steuerelement implementieren, das aufgeräumt werden muss, sollten Sie darüber nachdenken, sich in die Loaded y Unloaded Ereignisse stattdessen (oder zusätzlich).

D.h. Sie stellen eine Verbindung zu dem Ereignis in der Loaded Handler und trennen Sie die Verbindung in der Unloaded Bearbeiter. Natürlich ist dies nur eine Option, wenn Ihr Steuerelement das Ereignis nicht empfangen muss, während es nicht "geladen" ist, und wenn Sie viele Lade-/Entladezyklen korrekt unterstützen können.

Der Vorteil der Verwendung des Loaded / Unloaded Ereignisse ist, dass Sie die Benutzerkontrolle nicht überall, wo sie verwendet wird, manuell entsorgen müssen. Sie sollten sich jedoch bewusst sein, dass die Unloaded Ereignis wird nicht ausgelöst, nachdem die Anwendung heruntergefahren wurde. Wenn Ihr Shutdown-Modus z. B. OnMainWindowClose die Unloaded Ereignisse für andere Fenster werden nicht ausgelöst. Dies ist in der Regel jedoch kein Problem. Es bedeutet nur, dass Sie in einem Unloaded die vor/bei der Beendigung der Anwendung erfolgen müssen.

4voto

Yochai Timmer Punkte 46099

Wenn der Code den Destruktor erreicht hat, ist das nicht mehr wichtig.

Er wird nämlich nur zerstört, wenn er keine Ereignisse mehr anhört.
Wenn es noch auf die Ereignisse hören würde, wäre es nicht zerstört worden.

2voto

Damien_The_Unbeliever Punkte 227101

Ist PPMM etwas Externes mit einer längeren Lebensdauer als die MyControl Instanzen?

Wenn ja, es sei denn PPMM_FactorChanged ist eine statische Methode, die ppmmEventHandler wird einen Verweis auf den MyControl Instanz live - was bedeutet, dass die Instanz niemals für die Garbage Collection in Frage kommt und der Finalizer niemals ausgelöst wird.

Sie sollten nicht mehr die ppmmEventHandler um den Entfernungscode zu finden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X