1897 Stimmen

Richtige Verwendung der IDisposable-Schnittstelle

Ich weiß vom Lesen die Microsoft-Dokumentation dass die "primäre" Verwendung des IDisposable Schnittstelle ist es, nicht verwaltete Ressourcen zu bereinigen.

Für mich bedeutet "nicht verwaltet" Dinge wie Datenbankverbindungen, Sockets, Fensterhandles usw. Ich habe aber auch schon Code gesehen, bei dem die Dispose() Methode ist implementiert, um die verwaltet Ressourcen, was mir überflüssig erscheint, da der Garbage Collector dies für Sie erledigen sollte.

Zum Beispiel:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

Meine Frage ist, macht dies den Garbage Collector frei Speicher verwendet von MyCollection schneller als normalerweise?

edit : Bislang wurden einige gute Beispiele für die Verwendung von IDisposable zum Aufräumen von nicht verwalteten Ressourcen wie Datenbankverbindungen und Bitmaps vorgestellt. Aber nehmen wir an, dass _theList im obigen Code eine Million Zeichenfolgen enthielt und Sie diesen Speicher freigeben wollten jetzt anstatt auf den Garbage Collector zu warten. Würde der obige Code das erreichen?

2voto

supercat Punkte 72939

Ein Problem bei den meisten Diskussionen über "nicht verwaltete Ressourcen" ist, dass sie den Begriff nicht wirklich definieren, sondern zu implizieren scheinen, dass er etwas mit nicht verwaltetem Code zu tun hat. Es stimmt zwar, dass viele Arten von nicht verwalteten Ressourcen eine Schnittstelle zu nicht verwaltetem Code haben, aber es ist nicht hilfreich, den Begriff "nicht verwaltete Ressourcen" in diesem Sinne zu verstehen.

Stattdessen sollte man erkennen, was alle verwalteten Ressourcen gemeinsam haben: Bei allen geht es darum, dass ein Objekt ein äußeres "Ding" darum bittet, etwas in seinem Namen zu tun, zum Nachteil anderer "Dinge", und dass die andere Einheit zustimmt, dies bis auf weiteres zu tun. Wenn das Objekt aufgegeben wird und spurlos verschwindet, würde nichts dem äußeren "Ding" sagen, dass es sein Verhalten nicht mehr für das nicht mehr existierende Objekt ändern muss; folglich wäre der Nutzen des "Dings" dauerhaft gemindert.

Eine nicht verwaltete Ressource stellt also eine Vereinbarung eines externen "Dings" dar, sein Verhalten im Namen eines Objekts zu ändern, was die Nützlichkeit dieses externen "Dings" unnötig beeinträchtigen würde, wenn das Objekt aufgegeben würde und nicht mehr existierte. Eine verwaltete Ressource ist ein Objekt, das der Nutznießer einer solchen Vereinbarung ist, das sich jedoch dafür entschieden hat, eine Benachrichtigung zu erhalten, wenn es aufgegeben wird, und das eine solche Benachrichtigung nutzt, um seine Angelegenheiten in Ordnung zu bringen, bevor es zerstört wird.

2voto

Michael Burr Punkte 320591

Es gibt Dinge, die die Dispose() Operation in dem Beispielcode, der かもしれない eine Wirkung haben, die bei einer normalen GC nicht auftreten würde MyCollection Objekt.

Wenn die Objekte, auf die von _theList o _theDict von anderen Objekten referenziert werden, dann ist das List<> o Dictionary<> Objekt ist nicht Gegenstand der Sammlung, sondern hat plötzlich keinen Inhalt mehr. Gäbe es keine Dispose()-Operation wie im Beispiel, würden diese Sammlungen weiterhin ihren Inhalt enthalten.

Wenn dies der Fall wäre, würde ich es natürlich als fehlerhaftes Design bezeichnen - ich weise nur darauf hin (pedantisch, nehme ich an), dass die Dispose() nicht völlig überflüssig sein, je nachdem, ob es noch andere Verwendungen des List<> o Dictionary<> die in dem Fragment nicht enthalten sind.

2voto

Yuriy Zaletskyy Punkte 4775

Zunächst zur Definition. Für mich ist eine nicht verwaltete Ressource eine Klasse, die die Schnittstelle IDisposable implementiert oder etwas, das mit Hilfe von Aufrufen der DLL erstellt wurde. GC weiß nicht, wie man mit solchen Objekten umgeht. Wenn eine Klasse zum Beispiel nur Werttypen hat, dann betrachte ich diese Klasse nicht als Klasse mit nicht verwalteten Ressourcen. Für meinen Code befolge ich die folgenden Praktiken:

  1. Wenn die von mir erstellte Klasse einige nicht verwaltete Ressourcen verwendet, bedeutet dies, dass ich auch die Schnittstelle IDisposable implementieren sollte, um den Speicher zu reinigen.

  2. Reinigen Sie die Gegenstände, sobald ich sie benutzt habe.

  3. In meiner Dispose-Methode iteriere ich über alle IDisposable-Mitglieder der Klasse und rufe Dispose auf.

  4. In meiner Dispose-Methode rufe ich GC.SuppressFinalize(this) auf, um dem Garbage Collector mitzuteilen, dass mein Objekt bereits aufgeräumt wurde. Ich mache das, weil der Aufruf von GC eine teure Operation ist.

  5. Als zusätzliche Vorsichtsmaßnahme versuche ich, den mehrfachen Aufruf von Dispose() zu ermöglichen.

  6. Irgendwann füge ich das private Member _disposed hinzu und prüfe in Methodenaufrufen, ob das Objekt aufgeräumt wurde. Und wenn es aufgeräumt wurde, dann generiere ich ObjectDisposedException
    Die folgende Vorlage demonstriert, was ich in Worten als Codebeispiel beschrieben habe:

    public class SomeClass : IDisposable { /// <summary> /// As usually I don't care was object disposed or not /// </summary> public void SomeMethod() { if (_disposed) throw new ObjectDisposedException("SomeClass instance been disposed"); }

        public void Dispose()
        {
            Dispose(true);
        }
    
        private bool _disposed;
    
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;
            if (disposing)//we are in the first call
            {
            }
            _disposed = true;
        }
    }

1voto

controlbox Punkte 492

Der am meisten gerechtfertigte Anwendungsfall für die Entsorgung verwalteter Ressourcen ist die Vorbereitung des GC auf die Rückforderung von Ressourcen, die andernfalls nie gesammelt werden würden.

Ein Paradebeispiel sind zirkuläre Verweise.

Es ist zwar die beste Praxis, Muster zu verwenden, die zirkuläre Verweise vermeiden, aber wenn Sie (z. B.) ein "Kind"-Objekt haben, das einen Verweis zurück zu seinem "Elternteil" hat, kann dies die GC-Sammlung des Elternteils stoppen, wenn Sie den Verweis einfach aufgeben und sich auf GC verlassen - und wenn Sie einen Finalizer implementiert haben, wird er nie aufgerufen.

Die einzige Möglichkeit, dies zu umgehen, besteht darin, die zirkulären Verweise manuell zu unterbrechen, indem die Parent-Verweise auf den Children auf null gesetzt werden.

Die Implementierung von IDisposable für Eltern und Kinder ist der beste Weg, dies zu tun. Wenn Dispose auf dem Parent aufgerufen wird, rufen Sie Dispose auf allen Children auf, und setzen Sie in der Child-Dispose-Methode die Parent-Referenzen auf null.

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