Ist die Verwendung in einem Kontext angemessen, in dem nichts zu entsorgen ist?


9

In C # wird die usingAnweisung verwendet, um die Ressourcen deterministisch zu entsorgen, ohne auf den Garbage Collector zu warten. Zum Beispiel kann es verwendet werden, um:

  • Entsorgen Sie SQL-Befehle oder -Verbindungen.

  • Schließen Sie Streams und geben Sie die zugrunde liegende Quelle wie eine Datei frei.

  • Kostenlose GDI + Elemente,

  • etc.

Mir ist aufgefallen, dass dies usingimmer häufiger in Fällen verwendet wird, in denen nichts zu entsorgen ist, der Anrufer jedoch nur bequemer einen usingBlock als zwei separate Befehle schreiben kann .

Beispiele:

  • MiniProfiler , geschrieben vom Stack Overflow-Team, usingbezeichnet Blöcke im Profil:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }

    Ein alternativer Ansatz wäre, zwei Blöcke zu haben:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);

    Ein anderer Ansatz wäre die Verwendung von Aktionen:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC hat auch folgende usingFormulare ausgewählt:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>

Ist eine solche Verwendung angemessen? Wie kann man das rechtfertigen, da es mehrere Nachteile gibt:

  • Anfänger wären verloren, da eine solche Verwendung nicht der entspricht, die in Büchern und Sprachspezifikationen erklärt wird.

  • Der Code sollte ausdrucksstark sein. Hier leidet die Ausdruckskraft, da die angemessene Verwendung von darin usingbesteht, zu zeigen, dass sich dahinter eine Ressource wie ein Stream, eine Netzwerkverbindung oder eine Datenbank befindet, die freigegeben werden sollte, ohne auf den Garbage Collector zu warten.


2
Der Ansatz, zwei einzelne Anweisungen zu haben, hat das gleiche Problem wie das naive Ressourcenmanagement ohne using: Die letztere Anweisung (z. B. profiler.Stop(p)) kann angesichts von Ausnahmen und Kontrollabläufen nicht garantiert ausgeführt werden.

1
@delnan: Dies erklärt, warum MiniProfiler den zweiten Ansatz vermieden hat. Das zweite sollte jedoch in jedem Fall vermieden werden, da es schwierig zu schreiben und zu warten ist.
Arseni Mourzenko



Antworten:


7

Ihre letzte Aussage - dass "die angemessene Verwendung der Verwendung darin besteht, zu zeigen, dass sich dahinter eine Ressource wie ein Stream, eine Netzwerkverbindung oder eine Datenbank befindet, die freigegeben werden sollte, ohne auf den Garbage Collector zu warten", ist falsch, und der Grund dafür ist Weitere Informationen finden Sie in der Dokumentation zur IDisposable-Schnittstelle: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

Die Hauptverwendung dieser Schnittstelle besteht darin, nicht verwaltete Ressourcen freizugeben.

Wenn Ihre Klasse also nicht verwaltete Ressourcen verwendet, spielt es keine Rolle, wann Sie GC durchführen möchten oder nicht - es hat überhaupt nichts mit GC zu tun, da nicht verwaltete Ressourcen nicht GC-fähig sind ( https: // stackoverflow). com / Fragen / 3607213 / what-is-gemeint-by-Managed-vs-unmanaged-Ressourcen-in-net ).

So ist der Zweck der „Verwendung“ nicht auf GC zu vermeiden , ist warten, dann ist es eine Freisetzung von diesen nicht verwalteten Ressourcen zu zwingen , jetzt , bevor die Klasseninstanz Gültigkeitsbereich verlässt und Finalisierung der es heißt. Dies ist aus Gründen wichtig, die offensichtlich sein sollten: Eine nicht verwaltete Ressource kann Abhängigkeiten von anderen nicht verwalteten Ressourcen aufweisen, und wenn sie in der falschen Reihenfolge entsorgt (oder finalisiert) werden, können schlimme Dinge passieren.

Wenn die im using-Block instanziierte Klasse nicht verwaltete Ressourcen verwendet, lautet die Antwort "Ja" - dies ist angemessen.

Beachten Sie, dass IDisposable nicht normativ ist darüber sind nur für die Freigabe von nicht verwalteten Ressourcen, aber - nur , dass dies es ist primärer Zweck. Es kann vorkommen , dass der Autor der Klasse eine Aktion hat, die er zu einem bestimmten Zeitpunkt erzwingen möchte, und die Implementierung von IDisposable kann eine Möglichkeit sein, dies zu erreichen, aber ob dies eine elegante Lösung ist oder nicht, kann dies nur von Fall zu Fall beantwortet werden.

Unabhängig davon impliziert die Verwendung von "using", dass die Klasse IDisposable implementiert, sodass die Ausdruckskraft des Codes nicht verletzt wird. es macht ziemlich klar, was tatsächlich los ist.


"Die Hauptverwendung dieser Schnittstelle besteht darin, nicht verwaltete Ressourcen freizugeben." Ich sage, die Dokumentation von Microsoft ist in diesem Punkt scheiße. Vielleicht verwenden sie es so in der FCL, aber Anwendungsentwickler verwenden es seit fast einem Jahrzehnt für eine Vielzahl von Dingen - einige aus guten Gründen, andere aus nicht so guten Gründen. Das Zitieren dieses Teils ihres Dokuments beantwortet jedoch überhaupt nicht die Frage des OP.
Jesse C. Slicer

1
Hinweis mein vorletzter Absatz - der primäre Zweck nicht zu sein braucht nur Zweck. IDisposable kann aus irgendeinem Grund implementiert werden und bietet eine bekannte Schnittstelle mit erwartetem Verhalten und Nutzungsstil. Wenn Sie es also sehen, wissen Sie, dass etwas anderes passieren muss, bevor die Instanz einfach aus dem Gültigkeitsbereich entfernt werden kann. Der Punkt ist: Wenn es IDisposable implementiert, ist die Verwendung angemessen; alles andere ist nur Präambel.
Maximus Minimus

1
Sie sagen im Grunde, dass der Zweck nicht darin besteht, auf GC zu warten, sondern auf den Finalizer zu warten. Aber ich sehe keinen Unterschied: Der Finalizer wird vom GC aufgerufen.
Svick

Der GC arbeitet nicht deterministisch - Sie wissen nicht, wann er aufgerufen wird. Der GC ruft auch nur den Finalizer auf, entsorgt sich jedoch nicht selbst - die eigentliche Ressource selbst liegt außerhalb der Kontrolle des GC. Bei Verwendung von (1) wird das Objekt ordnungsgemäß erfasst, und (2) die Entsorgung erfolgt bekanntermaßen zu einem bekannten Zeitpunkt. Wenn der Bereich des Verwendungsblocks verlassen wird, ist die nicht verwaltete Ressource verschwunden (oder - im schlimmsten Fall - übergeben über etwas anderes, das es zerstören wird). Wirklich, das ist nur ein Trottel; Lesen Sie die Dokumentation, es ist alles, was es gibt, sollte nicht wiederholt werden müssen.
Maximus Minimus

8

Die Verwendung von usingimpliziert das Vorhandensein einer Dispose()Methode. Andere Programmierer gehen davon aus, dass eine solche Methode für das Objekt vorhanden ist. Wenn ein Objekt nicht wegwerfbar ist, sollten Sie es daher nicht verwenden using.

Code Klarheit ist König. Lassen Sie das Objekt entweder weg usingoder implementieren Sie es IDisposablefür das Objekt.

MiniProfiler scheint usingals Mechanismus zu dienen, um den zu profilierenden Code " einzäunen ". Dies hat einen gewissen Wert. Vermutlich ruft MiniProfiler entweder Dispose()auf, um einen Timer zu stoppen, oder der Timer wird gestoppt, wenn das MiniProfiler-Objekt den Gültigkeitsbereich verlässt.

Im Allgemeinen würden Sie aufrufen, usingwenn eine Art Finalisierung automatisch erfolgen muss. In der Dokumentation für html.BeginFormheißt es, dass bei Verwendung der Methode in einer usingAnweisung das schließende </form>Tag am Ende des usingBlocks gerendert wird.

Das bedeutet nicht unbedingt, dass es immer noch kein Missbrauch ist.


4
So weit so offensichtlich. Die Frage ist, wann (wenn überhaupt) es angebracht ist , etwas zu implementieren IDisposable/ Dispose()obwohl es nichts zu entsorgen hat, wie es OP beschreibt?

2
Ich dachte, ich hätte das klargestellt; Es gibt keine Zeit, in der dies angemessen ist.
Robert Harvey

1
Mein Punkt ist, Disposeist erforderlich using, um überhaupt Sinn zu machen (unabhängig davon, ob es angemessen ist). Die Beispiele von OP werden alle implementiert Dispose, nicht wahr? Und IIUC, die Frage ist, ob es richtig ist, dass diese Klassen Disposeanstelle einer anderen Methode (wie Stop()bei MiniProfiler) verwenden.

1
Ja, aber das ist nicht die Frage. Die Frage ist, ob dies eine gute Idee ist.

2
@delnan: Wieder dachte ich, ich hätte das klar gemacht. Wenn es die Absicht Ihres Codes klarer macht, ist es eine gute Idee. Wenn nicht, ist es nicht.
Robert Harvey

1

usingist fehler- und ausnahmesicher. Es stellt sicher Dispose(), dass unabhängig von Fehlern, die der Programmierer macht, aufgerufen wird. Es beeinträchtigt nicht das Abfangen oder Behandeln von Ausnahmen, aber die Dispose()Methode wird rekursiv im Stapel ausgeführt, wenn eine Ausnahme ausgelöst wird.

Objekte, die implementiert werden, IDisposeaber nichts zu entsorgen haben, wenn sie fertig sind. Sind bestenfalls zukunftssicher für ihr Design. Damit Sie Ihren Quellcode nicht umgestalten müssen, müssen sie in Zukunft über etwas verfügen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.