C # - Werden Objekte sofort zerstört, wenn sie den Bereich verlassen?

9

Kann ich darauf vertrauen, dass ein Objekt zerstört wird und sein Destruktor sofort aufgerufen wird, wenn es in C # den Gültigkeitsbereich verlässt?

Ich denke, da viele gängige Programmierpraktiken (z. B. Transaktionsobjekte) von diesem Verhalten abhängen, bin ich jedoch nicht sehr mit der Garbage Collection vertraut und habe wenig Verständnis dafür, wie sich solche Sprachen normalerweise verhalten.

Danke.

    
sharkin 26.09.2009, 09:45
quelle

6 Antworten

25

Nein, .Net und daher C # beruhen auf einer Speicherverwaltung für Speicherbereinigung. So werden Destruktoren (die in .Net Finalizer genannt werden) nicht aufgerufen, bis GC es richtig findet, die Objekte zu zerstören.

Zusätzlich: die meisten "normalen" Objekte in C # haben keine Destruktoren. Wenn Sie das Destruktormuster benötigen, sollten Sie die IDisposable-Schnittstelle mit dem Dispose-Muster . Bei Einwegobjekten sollten Sie außerdem sicherstellen, dass die Dispose-Methode entweder mit dem Keyword aufgerufen wird oder direkt die Methode aufrufen.

Um (hoffentlich) deutlicher zu machen: Deterministische Entsorgung ist in .Net nützlich, z. Wenn Sie explizit Ressourcen freigeben müssen, die nicht von der .NET-Laufzeitumgebung verwaltet werden. Beispiele für solche Ressourcen sind Dateihandles, Datenbankverbindungen usw. Es ist normalerweise wichtig, dass diese Ressourcen freigegeben werden, sobald sie nicht mehr benötigt werden. Daher können wir es uns nicht leisten, auf die Freilassung des GC zu warten.

Um in der nicht-deterministischen Welt des .NET-GC eine deterministische Beseitigung (ähnlich dem Gültigkeitsbereich-Verhalten von C ++) zu erhalten, verwenden die .NET-Klassen die IDisposable-Schnittstelle. Aus dem Dispose-Muster finden Sie hier einige Beispiele:

Wenn Sie zum ersten Mal eine Einwegressource instanziieren und dann das Objekt außerhalb des Gültigkeitsbereichs lassen, wird es dem GC überlassen, das Objekt zu entsorgen:

%Vor%

Um dies zu beheben, können wir das Objekt explizit disponieren:

%Vor%

Aber was passiert, wenn zwischen Zeile 2 und 6 etwas schief läuft? Dispose wird nicht aufgerufen. Um sicherzustellen, dass Dispose schließlich unabhängig von Ausnahmen aufgerufen wird, können wir Folgendes tun:

%Vor%

Da dieses Muster häufig benötigt wird, enthält C # zur Vereinfachung das Schlüsselwort using. Das folgende Beispiel entspricht dem obigen:

%Vor%     
Peter Lillevold 26.09.2009, 09:48
quelle
8

Nein. Ein -Objekt geht nicht "außerhalb des Gültigkeitsbereichs", der Verweis darauf (d. H. Die Variable, mit der Sie darauf zugreifen).

Sobald keine Verweise auf ein bestimmtes Objekt mehr vorhanden sind, wird dieses Objekt für die Garbage Collection (GC), falls erforderlich, geeignet . Wenn der GC entscheidet, dass er den Speicherplatz für das Objekt zurückerhält, auf das nicht mehr verwiesen wird, wird der Objektfinalizer aufgerufen.

Wenn Ihr Objekt eine Ressource ist (z. B. ein Dateihandle, eine Datenbankverbindung), sollte es die IDisposable-Schnittstelle implementieren (die das Objekt dazu verpflichtet, eine Dispose() -Methode zu implementieren, um offene Verbindungen usw. zu bereinigen). Die beste Vorgehensweise für Sie wäre in diesem Fall, das Objekt als Teil eines Blocks using zu erstellen, so dass Ihre Anwendung nach Abschluss dieses Blocks automatisch die Objekte Dispose() method aufruft, die sich um das Schließen Ihres Objekts kümmern Datei / db Verbindung / was auch immer.

z.B.

%Vor%

Der using -Block ist nur ein Teil des syntaktischen Zuckers, der Ihre Interaktionen mit dem conn -Objekt in einem try -Block umschließt, zusammen mit einem finally -Block, der nur conn.Dispose()

aufruft     
AgentConundrum 26.09.2009 09:57
quelle
4

Es gibt keine solche Sache als C ++ - ähnlicher Destruktor in C #. (Es gibt ein anderes Konzept von Destruktoren in C #, auch Finalizer genannt, das die gleiche Syntax wie C ++ - Destruktoren verwendet, aber nicht zum Zerstören von Objekten. Sie sollen einen Aufräummechanismus für nicht verwaltete Ressourcen bereitstellen.) Der Garbage Collector bereinigt Objekte , nachdem sie nicht mehr referenziert wurden . Nicht sofort, und es gibt auch keine Garantie dafür.

Zum Glück gibt es auch keinen wirklichen Grund, warum Sie das garantieren wollen. Wenn Sie den Speicher benötigen, wird der GC ihn dann zurückfordern. Wenn Sie das nicht tun, warum sollten Sie sich in Acht nehmen, wenn noch irgendein Müllobjekt in der Nähe ist? Es ist kein Speicherleck: Der GC kann ihn immer noch finden und aufräumen.

    
Joren 26.09.2009 09:49
quelle
4

Nein, das ist nicht garantiert. Ähnlich wie in Java wird in C # der Garbage Collector ausgeführt, wenn er benötigt wird (z. B. wenn der Heap zu voll wird). Wenn Ihre Objekte jedoch IDisposable implementieren, wird i. e. Sie haben eine Dispose() -Methode und müssen aufgerufen werden, dann können Sie das using -Schlüsselwort nutzen:

%Vor%

Auf diese Weise wird Dispose() sofort aufgerufen, wenn dieser using Block verlassen wird.

Hinweis: IDisposable wird in vielen Typen gefunden, vor allem GDI +, aber auch Datenbankverbindungen, Transaktionen usw., so dass es hier wirklich das richtige Muster ist.

Hinweis 2: Hinter den Kulissen wird der Block in einen try / finally Block übersetzt:

%Vor%

Aber diese Übersetzung wird vom Compiler gemacht und ist sehr praktisch, um Dispose() nicht zu vergessen.

    
Joey 26.09.2009 09:50
quelle
0

Ich denke nicht, dass Sie sich auf Müllsammler verlassen sollten. Selbst wenn Sie ablesen, wie sie funktionieren, könnte es sehr wohl sein, dass sie es in der nächsten Version erneut implementiert haben.

Auf jeden Fall werden Objekte nicht in dem Moment gesammelt, in dem Sie sie nicht mehr beachten. In der Regel werden sie gesammelt, bis ein bestimmter Schwellenwert erreicht ist, und dann werden sie freigegeben.

Besonders in Java-Programmen ist dies sehr auffällig, wenn Sie sich den Speicherverbrauch des Task-Managers anschauen. Es wächst und wächst und plötzlich fällt es jede Minute wieder ab.

    
Toad 26.09.2009 09:49
quelle
0

Nein. Wenn Sie sich auf die CLI-Spezifikation beziehen (S. 8.9.6.7 über Finalizer) Ссылка finden Sie die folgenden

  

Die CLI sollte sicherstellen, dass die Finalizer bald nach dem Auftreten der Instanz aufgerufen werden   nicht zugänglich. Während auf Speicherdruck zu verlassen   Trigger Finalisierung ist akzeptabel, Implementierer sollten die Verwendung von zusätzlichen berücksichtigen   Metriken

aber es muss nicht.

    
Andrey Taptunov 26.09.2009 09:55
quelle

Tags und Links