none
try-finally-Blöcke für Erstellung von Ressourcen zur Laufzeit - sinnvoll? RRS feed

  • Frage

  • Hallo,

    in Delphi bin ich es gewohnt, bei der Verwendung von zur Laufzeit erstellten Ressourcen (Komponenten etc) try-finally-Blöcke zu verwenden.

    Beispiel:

    PrjFile = XmlWriter.Create(sFileName, settings);
    				
    try {
       PrjFile.WriteStartElement("Test");
    
       ...
    
       PrjFile.WriteEndElement();
       PrjFile.Close();
    } 
    finally 
    {
       PrjFile = null;	
    }
    
    Sollte ich diese Angewohnheit in C# beibehalten oder kann ich mir das getrost abgewöhnen?

    Donnerstag, 19. August 2010 10:14

Antworten

  • Hallo Heiko,

    wenn Du die Ausnahme nicht behandeln willst, sondern nur sicherstellen,
    dass der Stream geschlossen wird, bietet sich die using Anweisung an,
    da Stream-basierte Klassen die IDisposable Schnittstelle anbieten.

    Der Code verkürzt sich wie im Erstellen von XML-Writern gezeigt:

    using (var PrjFile = XmlWriter.Create(sFileName, settings))
    {
      PrjFile.WriteStartElement("Test");
      // ...
      PrjFile.WriteEndElement();
    } 
    
    

    Da bei Streams Close selbst Dispose aufruft, ist ein zusätzliches Close nicht erforderlich.

    Gruß Elmar

    • Als Antwort markiert Heiko Adams Donnerstag, 19. August 2010 12:57
    Donnerstag, 19. August 2010 10:49
  • Hallo Heiko,

    sobald das Ende des Using-Blocks erreicht ist wird dieDispose-Funktion der genutzten Klasse aktiviert, somit ist hier in jedem Fall sichergestellt, dass nach dem Verlassen des using Blocks die Klasse aufgelöst wird.

    Du kannst natürlich innerhalb des Using-Blocks weiter hin mit Try-Catch arbeiten um sicher zu gehen, dass deine Schreibzugriffe erfolgreich ausgeführt werden.


    MfG, Sebastian Gross
    • Als Antwort markiert Heiko Adams Donnerstag, 19. August 2010 12:57
    Donnerstag, 19. August 2010 12:51

Alle Antworten

  • Hallo Heiko,

    wenn Du die Ausnahme nicht behandeln willst, sondern nur sicherstellen,
    dass der Stream geschlossen wird, bietet sich die using Anweisung an,
    da Stream-basierte Klassen die IDisposable Schnittstelle anbieten.

    Der Code verkürzt sich wie im Erstellen von XML-Writern gezeigt:

    using (var PrjFile = XmlWriter.Create(sFileName, settings))
    {
      PrjFile.WriteStartElement("Test");
      // ...
      PrjFile.WriteEndElement();
    } 
    
    

    Da bei Streams Close selbst Dispose aufruft, ist ein zusätzliches Close nicht erforderlich.

    Gruß Elmar

    • Als Antwort markiert Heiko Adams Donnerstag, 19. August 2010 12:57
    Donnerstag, 19. August 2010 10:49
  • Also wenn ich ehrlich bin, gefällt mir mein gewohnter try-finally-Block da doch besser, da so sichergestellt ist, das die Instanz auch im Falle einer Ausnahme wieder aufgelöst wird. Oder verhält sich using da identisch?
    Donnerstag, 19. August 2010 12:21
  • Hallo Heiko,

    sobald das Ende des Using-Blocks erreicht ist wird dieDispose-Funktion der genutzten Klasse aktiviert, somit ist hier in jedem Fall sichergestellt, dass nach dem Verlassen des using Blocks die Klasse aufgelöst wird.

    Du kannst natürlich innerhalb des Using-Blocks weiter hin mit Try-Catch arbeiten um sicher zu gehen, dass deine Schreibzugriffe erfolgreich ausgeführt werden.


    MfG, Sebastian Gross
    • Als Antwort markiert Heiko Adams Donnerstag, 19. August 2010 12:57
    Donnerstag, 19. August 2010 12:51
  • Hallo,

    Das using verhält sich dort richtig, denn Dispose wird auch bei einer Ausnahme aufgerufen.
    Es entspricht manuell codiert in etwa (siehe auch Beschreibung zur using Anweisung):

    XmlWriter PrjFile = PrjFile = XmlWriter.Create(...);
    try
    {
      PrjFile.WriteStartElement("Test");
      // ...
      PrjFile.WriteEndElement();
    }
    finally
    {
      if (prjFile != null)
       ((IDisposable)prjFile).Dispose();
    }
    Und ist "richtiger" als Deine derzeitige Vorgehensweise, wo die Instanz eben nicht (sofort) "aufgelöst" wird.

    Denn es reicht nicht "prjFile" im finally auf null zu setzen. Bei einer Ausnahme wird Close im try gar nicht erst erreicht.
    Da Du "nur" null setzt, bleibt die Datei eröffnet und wird erst bei der nächsten Garbage Collection freigegeben.
    Das Wann steht jedoch in den Sternen, und so kann es Folgefehler nach sich ziehen, weil die Datei noch geöffnet ist.
    (Und das gilt nur, wenn PrjFile die einzige Referenz ist, sonst kann es auch ewig dauern).

    Ich empfehle Dir:
    Gewöhne Dir using dort an, wo die Klasse IDisposable implementiert
    und Du bisher try finally verwendet hast.

    Gruß Elmar

    Donnerstag, 19. August 2010 13:12
  • Gewöhne Dir using dort an, wo die Klasse IDisposable implementiert und Du bisher try finally verwendet hast.

    Und was mache ich bei Klassen, die nicht IDisposable implementieren? Da bietet sich für eine halbwegs sichere Programmierung doch eigentlich nur ein Vorgehen wie in meinem Eingangsposting an?? Oder stehe ich grade auf dem Schlauch?
    Dienstag, 24. August 2010 10:19
  • Hallo,

    wenn die Klasse es nicht implementiert, so benötigt sie (im Idealfall) keine Sonderbehandlung.
    Idealfall insofern, als eine richtige Implementation vorausgesetzt wird,
    was bei .NET Framework-Klassen gegeben sein sollte -
    bei anderen Quellen sollte/muß man manchmal genauer hinschauen.

    Die Kernaufgabe von IDisposable ist es unmanaged Ressourcen freizugeben.
    Klassen, die vollständig managed sind und keine Verweise auf unmanaged Ressourcen
    (hier waren es die Dateihandle, die vom Betriebsystem kommen) werden es nur in
    Ausnahmefällen implementieren.
    Dort kann man sich auf die Freigabe durch den Garbage Collector verlassen.

    Und natürlich bleibt immer try ... finally für die Stellen,
    bei denen man einen Zustand wiederherstellen möchte -
    und kann dies mit using mischen, wenn beides zusammenkommt.

    Weiteres zu IDisposable aus allen Blickwinkeln findest Du u. a. bei Stackoverflow

    Gruß Elmar

    Dienstag, 24. August 2010 10:54
  • Meine Frage bezog sich eher auf die Verwendung von COM-Schnittstellen.

    Aber dafür mache ich wohl besser einen seperaten Thread auf

    Dienstag, 24. August 2010 11:50
  • Hallo Heiko,

    das ist wäre eine weitere "Baustelle".

    IDisposable sollte man damit keinesfalls verwechseln,
    Eine deterministische Finalisierung kennt .NET nicht,
    ebensowenig wie Verweiszählern (via IUnknown).

    Dort kümmert sich unter normalen Umständen ebenfalls die Runtime
    über COM Wrapper um die Verwaltungsarbeiten und die Freigabe.
    In einigen (nicht sauberen Umgebungen) kann es notwendig sein
    dies explizit zu tun, in dem man Marshal. ReleaseComObject aufruft.
    Zur Regel sollte man das aber nicht machen, siehe dazu den Artikel:
    Marshal.ReleaseComObject Considered Dangerous
    wo auch der ältere erwähnt wird:
    http://blogs.msdn.com/b/cbrumme/archive/2003/04/16/51355.aspx

    der den Unterschied zu IDisposable erläutert.

    Gruß Elmar

    Dienstag, 24. August 2010 13:45