Kurze Fragen zu using(...) { ... }


  • Administrator

    FileStream f;
    
    using(FileStream file = new FileStream("./test.txt", FileMode.Open))
    {
      f = file;
    }
    
    Console.WriteLine(f.Length);
    

    Kann man dies als undefiniert wie in C++ ansehen? Oder was wird hier passieren? Ging mir gerade durch den Kopf. In meinen Testprogramm hat es einen Absturz verursacht, daher nehme ich mal an, dass es undefiniert ist?

    Und noch eine kurze Frage:
    Gibt es eigentlich eine Möglichkeit mehrere Objekte zu definieren? Also ca. so:

    using(FileStream file1 = new FileStream("./test1.txt", FileMode.Open),
          FileStream file2 = new FileStream("./test2.txt", FileMode.Open),
          FileStream file3 = new FileStream("./test3.txt", FileMode.Open),
          /* usw. */)
    {
    }
    

    Grüssli



  • Da ist nichts undefiniert. Du hast in f ne Referenz auf den Stream den du im using Block erstellst. Nach Verlassen des Blocks wird vom Stream Close, bzw. Dispose aufgerufen und er damit geschlossen, und damit ist klar dass sobald du versuchst f außerhalb vom using zu verwenden, es krachen muss.

    Zu der zweiten Frage. Mach sowas:

    using(...) {
        using(...) {
            using(...) {
            }
        }
    }
    


  • Dravere schrieb:

    using(FileStream file1 = new FileStream("./test1.txt", FileMode.Open),
          FileStream file2 = new FileStream("./test2.txt", FileMode.Open),
          FileStream file3 = new FileStream("./test3.txt", FileMode.Open),
          /* usw. */)
    {
    }
    

    =>

    using(FileStream file1 = new FileStream("./test1.txt", FileMode.Open),
          file2 = new FileStream("./test2.txt", FileMode.Open),
          file3 = new FileStream("./test3.txt", FileMode.Open),
          /* usw. */)
    {
    }
    

  • Administrator

    Talla schrieb:

    Da ist nichts undefiniert. ..., es krachen muss.

    Das würde ich sehr wohl als undefiniert bezeichnen 🙂
    Oder fliegt da eine Exception? Oder gibt es einfach ein abort oder sowas? Also was passiert im Hintergrund, wenn es definiert ist?

    Talla schrieb:

    Zu der zweiten Frage. Mach sowas:

    using(...) {
        using(...) {
            using(...) {
            }
        }
    }
    

    Nicht sehr schön 🙂

    @witte,
    Und falls es unterschiedliche Typen wären? Dann muss ich wohl die Lösung von Talla verwenden?

    Naja, wüsste jetzt allerdings sowieso keinen Anwendungsfall. War mehr eine theoretische Frage aus Interesse.

    Grüssli



  • Dravere schrieb:

    Und falls es unterschiedliche Typen wären? Dann muss ich wohl die Lösung von Talla verwenden?

    Ja. Du kannst aber auch daraus eine "Ressourcenklasse" bauen die IDisposable implementiert, die dann im Konstruktor die drei Objekte erstellt und im Dispose entfernt. Dann brauchst Du nur ein using-Block für das Objekt Deiner Ressourcenklasse wenn Dich das Aussehen stört...


  • Administrator

    witte schrieb:

    Dravere schrieb:

    Und falls es unterschiedliche Typen wären? Dann muss ich wohl die Lösung von Talla verwenden?

    Ja. Du kannst aber auch daraus eine "Ressourcenklasse" bauen die IDisposable implementiert, die dann im Konstruktor die drei Objekte erstellt und im Dispose entfernt. Dann brauchst Du nur ein using-Block für das Objekt Deiner Ressourcenklasse wenn Dich das Aussehen stört...

    if(BrainModus.SLEEP == brain.Modus)
    {
      brain.activate();
    }
    

    😃
    Danke! Manchmal sieht man echt das offensichtlichste einfach nicht 🙂

    Grüssli



  • Dravere schrieb:

    Talla schrieb:

    Da ist nichts undefiniert. ..., es krachen muss.

    Das würde ich sehr wohl als undefiniert bezeichnen 🙂
    Oder fliegt da eine Exception? Oder gibt es einfach ein abort oder sowas? Also was passiert im Hintergrund, wenn es definiert ist?

    Es gibt eine ObjectDisposedException. Siehe auch: http://msdn.microsoft.com/en-us/library/system.io.stream.length.aspx


  • Administrator

    O.o schrieb:

    Es gibt eine ObjectDisposedException. Siehe auch: http://msdn.microsoft.com/en-us/library/system.io.stream.length.aspx

    Die habe ich auch gesehen, aber der Text dahinter hat mich ein wenig verwirrt:

    MSDN schrieb:

    ObjectDisposedException - Methods were called after the stream was closed.

    In der Beschreibung steht nichts, dass diese Exception geworfen wird, wenn das Objekt ungültig ist. Oder versteh ich da was an using verkehrt?
    Was genau passiert, wenn der using-Block verlassen wird? Wird das Objekt im GC nicht zerstört? Sondern nur die Methode Dispose aufgerufen und das Objekt ist weiterhin gültig?

    Grüssli



  • Wenn der using -Block verlassen wird wird automatische IDisposable.Dispose() aufgerufen. Das hat zur Folge, dass alle unverwalteten Ressourcen freigegeben werden und die Finalisierung des Objektes durch GC.SupressFinalize(this) unterdrückt wird, da das Objekt ja bereits alle unverwalteten Ressourcen freigegeben hat. Desweiteren hat es zur Folge, dass die meisten Aufrufe von Methoden und Eigenschaften des Objektes in einer ObjectDisposedException enden (welche genau eine Excepetion werfen hängt von der Implementierung ab; Stream.ToString ist z.B. weiterhin möglich). Die Referenz auf das Objekt ist also weiterhin gültig, aber bei den "Hauptfunktionen" des Objektes wird es dir zu 99% eine ObjectDisposedException um die Ohren knallen.

    Siehe dazu auch: MSDN: IDisposable.Dispose Method bzw. MSDN: Implementing Finalize and Dispose to Clean Up Unmanaged Resources



  • Dravere schrieb:

    Was genau passiert, wenn der using-Block verlassen wird? Wird das Objekt im GC nicht zerstört? Sondern nur die Methode Dispose aufgerufen und das Objekt ist weiterhin gültig?

    Nein, der Speicher vom Objekt wird nicht vom GC freigegeben. Beim Verlassen des using Blocks wird einfach nur Dispose aufgerufen. Das Objekt gibt im Dispose jetzt seine umnanaged Ressourcen frei (oder macht was auch immer es will). Dadurch das Dispose aber auch im Fehlerfall (sprich irgend ne Exception im Using Block) aufgerufen wird, wird sichergestellt, dass das verwendete Objekt immer seine unmanaged Ressourcen freigibt. Das eigentliche Objekt existiert weiterhin genauso wie vorher auch. Aber, im Regelfall, gerät das Objekt ja durch verlassen des Blocks außerhalb des Sichtbarkeitsbereiches und kann dann von dem GC bei Gelegenheit entfernt werden. Du hälst aber weiterhin ne Referenz auf das Objekt und hinderst den GC das Objekt zeitnah bei Verlassen des Blockes zu entsorgen. Ne Referenz auf ein im Using Block verwendetes Objekt halten zu wollen macht absolut keinen Sinn, darauf nach dem using Block auch noch zugeifen zu wollen noch viel weniger.


  • Administrator

    Danke für die Aufklärung. Das habe ich in meinem Buch ganz eindeutig missverstanden. Hatte mich darüber schon ein wenig gewundert gehabt, da man nämlich sonst ein wenig den GC aushebeln würde. Aber jetzt ist alles klar und der GC ist auch nicht ausgehebelt und man hat nur eine sichere zusätzliche Möglichkeit. 🙂

    Nochmals danke.

    Grüssli


Anmelden zum Antworten