Mit C # kann ich Folgendes tun (Beispiel aus MSDN):
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
Was passiert bei font4 = new Font
Würfen? Soweit ich weiß, wird font3 Ressourcen verlieren und nicht entsorgt werden.
- Ist das wahr? (font4 wird nicht entsorgt)
- Bedeutet dies,
using(... , ...)
dass eine verschachtelte Verwendung insgesamt vermieden werden sollte?
c#
using
using-statement
Benjamin Gruenbaum
quelle
quelle
using(... , ...)
es trotzdem mit Blöcken verschachtelt wird, aber das weiß ich nicht genau.using
, wird der GC es schließlich sammeln.finally
Block kompiliert , wäre es nicht in den Block eingetreten, bis alle Ressourcen erstellt worden wären.using
in atry
-finally
der Initialisierungsausdruck außerhalb von ausgewertet wirdtry
. Es ist also eine vernünftige Frage.Antworten:
Nein.
Der Compiler generiert
finally
für jede Variable einen eigenen Block.Die Spezifikation (§8.13) sagt:
quelle
UPDATE : Ich habe diese Frage als Grundlage für einen Artikel verwendet, der hier zu finden ist . Weitere Informationen zu diesem Thema finden Sie hier. Danke für die gute Frage!
Obwohl die Antwort von Schabse natürlich richtig ist und die gestellte Frage beantwortet, gibt es eine wichtige Variante Ihrer Frage, die Sie nicht gestellt haben:
Lassen Sie mich das etwas klarer machen. Angenommen, wir haben:
Jetzt haben wir
Dies ist das gleiche wie
OK. Angenommen,
Whatever
Würfe. Dann wird derfinally
Block ausgeführt und die Ressource freigegeben. Kein Problem.Angenommen,
Blah1()
Würfe. Dann erfolgt der Wurf, bevor die Ressource zugewiesen wird. Das Objekt wurde zugewiesen, aber der ctor kehrt nie zurück, wird alsofoo
nie ausgefüllt. Wir haben das nie eingegeben,try
also geben wir auch nie das einfinally
. Die Objektreferenz wurde verwaist. Schließlich wird der GC dies feststellen und in die Finalizer-Warteschlange stellen.handle
ist immer noch Null, also macht der Finalizer nichts. Beachten Sie, dass der Finalizer gegenüber einem Objekt, das finalisiert wird und dessen Konstruktor nie abgeschlossen wurde, robust sein muss . Sie müssen so starke Finalizer schreiben. Dies ist ein weiterer Grund, warum Sie das Schreiben von Finalisierern Experten überlassen und nicht versuchen sollten, dies selbst zu tun.Angenommen,
Blah3()
Würfe. Der Wurf erfolgt nach der Zuweisung der Ressource. Aber auch hierfoo
wird nie ausgefüllt, wir geben das nie einfinally
und das Objekt wird vom Finalizer-Thread bereinigt. Diesmal ist der Griff ungleich Null und der Finalizer bereinigt ihn. Wieder läuft der Finalizer auf einem Objekt, dessen Konstruktor nie erfolgreich war, aber der Finalizer wird trotzdem ausgeführt. Offensichtlich muss es, denn diesmal hatte es Arbeit zu erledigen.Nehmen wir nun an,
Blah2()
wirft. Der Wurf erfolgt, nachdem die Ressource zugewiesen, aber vorherhandle
ausgefüllt wurde! Wieder läuft der Finalizer, aber jetzthandle
ist er immer noch Null und wir lecken den Griff!Sie müssen äußerst cleveren Code schreiben , um dieses Leck zu verhindern.
Font
Wen interessiert zum Teufel Ihre Ressource? Wir lecken einen Font-Handle, große Sache. Wenn Sie jedoch unbedingt verlangen, dass jede nicht verwaltete Ressource bereinigt wird, unabhängig vom Zeitpunkt der Ausnahmen , haben Sie ein sehr schwieriges Problem.Die CLR muss dieses Problem mit Sperren lösen. Seit C # 4 wurden Sperren, die die
lock
Anweisung verwenden, folgendermaßen implementiert:Enter
wurde sehr sorgfältig geschrieben, so dass unabhängig davon, welche Ausnahmen ausgelöst werden ,lockEntered
genau dann auf true gesetzt wird, wenn die Sperre tatsächlich aufgehoben wurde. Wenn Sie ähnliche Anforderungen haben, müssen Sie tatsächlich schreiben:und schreibe
AllocateResource
geschicktMonitor.Enter
so, dass unabhängig davon, was im Inneren passiertAllocateResource
, das genau dannhandle
ausgefüllt wird, wenn es freigegeben werden muss.Die Beschreibung der Techniken hierfür würde den Rahmen dieser Antwort sprengen. Wenden Sie sich an einen Experten, wenn Sie diese Anforderung haben.
quelle
Blah
Methodenaufrufe. Was verhindert, dass an einem dieser Punkte eine ThreadAbortException auftritt?AllocateResource
aber vor der Zuweisung an aufx
. AnThreadAbortException
diesem Punkt kann A passieren. Jedem hier scheint mein Punkt zu fehlen, nämlich die Erstellung einer Ressource und die Zuordnung eines Verweises darauf zu einer Variablen ist keine atomare Operation . Um das von mir identifizierte Problem zu lösen, müssen Sie es zu einer atomaren Operation machen.Als Ergänzung zur Antwort von @SLaks finden Sie hier die IL für Ihren Code:
Beachten Sie die verschachtelten try / finally-Blöcke.
quelle
Dieser Code (basierend auf dem Originalbeispiel):
Es wird die folgende CIL erstellt (in Visual Studio 2013 für .NET 4.5.1):
Wie Sie sehen,
try {}
startet der Block erst nach der ersten Zuordnung, die um stattfindetIL_0012
. Auf den ersten Blick diese nicht erscheinen das erste Element in ungeschützten Code zuzuweisen. Beachten Sie jedoch, dass das Ergebnis an Position 0 gespeichert ist. Wenn die zweite Zuordnung dann fehlschlägt, wird der äußerefinally {}
Block ausgeführt, und dies holt das Objekt von Position 0, dh der ersten Zuordnung vonfont3
, und ruft seineDispose()
Methode auf.Interessanterweise ergibt das Dekompilieren dieser Assembly mit dotPeek die folgende rekonstituierte Quelle:
Der dekompilierte Code bestätigt, dass alles korrekt ist und dass der Code im
using
Wesentlichen zu verschachteltenusing
s erweitert ist. Der CIL-Code ist etwas verwirrend anzusehen, und ich musste ihn ein paar Minuten lang anstarren, bevor ich richtig verstanden habe, was passiert ist. Daher wundert es mich nicht, dass einige Geschichten über „alte Frauen“ aufgetaucht sind Dies. Der generierte Code ist jedoch die unangreifbare Wahrheit.quelle
Hier ist ein Beispielcode, um die Antwort von @SLaks zu beweisen:
quelle
font4 = new Font
Würfe ausgeführt werden? Soweit ich weiß, werden in font3 Ressourcen verloren gehen und nicht entsorgt."