Autor Beitrag
FrankyBoy69
Hält's aus hier
Beiträge: 2



BeitragVerfasst: So 26.04.09 16:41 
Hallo *,

ich bin neu bei C# und habe folgende Klasse erstellt:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;

namespace MyWriter
{
    class MyWriter
    {
            private StreamWriter _sr = null;

            //Debug
            private static int _ctor_cnt = 0;
            private static int _dtor_cnt = 0;

            public MyWriter(string fileName)
            {
                _ctor_cnt++;
                _sr = new StreamWriter(fileName);
            }

            ~MyWriter()
            {
                _dtor_cnt++;


                Console.WriteLine("Ctor wurde {0} mal und Dtor {1} mal aufgerufen.", _ctor_cnt, _dtor_cnt);

                if (_sr != null)
                {
                    _sr.Close();
                }
            }

            public void Write(string theData)
            {
                _sr.WriteLine(theData);
                _sr.Flush();
            }
    }
}


Verwendet wird die Klasse von der StandardForm so:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
private void butMyWriterTest_Click(object sender, EventArgs e)
        {
            MyWriter mw = new MyWriter("C:\\temp\\mw.txt");
            mw.Write("Das soll eine Zeile werden.");
        }


Nun zu meinen Problem:
Wenn ich die Form1 (und somit das Programm) beende, wird eine Exception ausgelöst bei folgendem Befehl: _sr.Close();
Details zur Exception:
Zitat:
System.ObjectDisposedException wurde nicht behandelt.
Message="Auf eine geschlossene Datei kann nicht zugegriffen werden."
Source="mscorlib"
ObjectName=""
StackTrace:
bei System.IO.FileStream.Flush()
bei System.IO.StreamWriter.Dispose(Boolean disposing)
bei System.IO.StreamWriter.Close()
bei MyWriter.MyWriter.Finalize()
InnerException:


Falls ich das Ganze richtig verstehe, wird versucht an einen bereits geschlossenen Stream Close() aufzurufen. Was ich aber nicht verstehe ist: Wer hat denn den Stream geschlossen?
Ich ging davon aus, dass während der gesamten Lebenszeit der Klasse auch der Stream lebt. Und dann halt im Destruktor geschlossen werden muss.

Kann mir hier jemand auf die Sprünge helfen?

Danke im Voraus :shock:
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: So 26.04.09 16:51 
Hallo und :welcome:!

Wenn ich das richtig sehe, wird während der Laufzeit Deines Programmes schon die Dispose-Methode Deines StreamWriters durch die Garbage Collection aufgerufen und dann beim Schließen des Programmes nochmals Close durch Deinen Finalizer.

Da Deine Klasse via StreamWriter unmanaged Resources belegt, solltest Du in Deiner Klasse den IDisposable-Pattern implementieren. Darin ist dann auch der Aufruf von GC.SuppressFinalize() enthalten, welcher obiges Problem lösen sollte.

Zu dem Pattern dürfte Google einiges liefern.

Grüße
Christian

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: So 26.04.09 17:33 
user profile iconFrankyBoy69 hat folgendes geschrieben Zum zitierten Posting springen:
Und dann halt im Destruktor geschlossen werden muss.
Nein, der Desktruktor ist allein zum Aufräumen von unmanaged Ressourcen da. Wie du gesehen hast, darfst du darin nicht einmal auf managed Ressourcen zugreifen, da die Reihenfolge der Finalize-Aufrufe unbestimmt ist.

Solange du also nur andere CLR-Klassen wie den StreamWriter benutzt, brauchst du überhaupt keinen Destruktor. Du kannst dem Benutzer deiner Klasse aber helfen, knappe Ressourcen wie IO-Handles schneller (determiniert) freizugeben, indem du IDisposable implementierst und darin dann Dispose aller deiner managed Ressourcen aufrufst.

Edit: Es gibt noch das Pattern, dass eine Basisklasse IDisposable und einen Destruktor implementiert und beides an eine protected override void Dispose(bool disposing) weiterleitet: msdn.microsoft.com/e...ibrary/fs2xkftw.aspx. IMHO aber unnötig, solange keine unmanaged Ressourcen freigegeben werden sollen (auch nicht in Subklassen), auch wenn ich langsam selbst nicht mehr durchblicke :lol: .

_________________
>λ=
FrankyBoy69 Threadstarter
Hält's aus hier
Beiträge: 2



BeitragVerfasst: So 26.04.09 18:15 
Hallo und danke für eure Antworten.
Ich bin erst einmal dem Ratschlag von Christian gefolgt und habe das Interface IDisposable implementiert.
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
            #region IDisposable Member

            public void Dispose()
            {
                if (_sr != null )
                {
                    // implicite call of Dispose(true)
                    _sr.Close();
                }

                Console.WriteLine("Object disposed.");               

                // Indicate that the instance has been disposed.
                _sr = null;

                GC.SuppressFinalize(this);
            }

            #endregion


Nun kommt keine Fehlermeldung mehr und das File wird wie erwartet geschrieben. Allerdings kommt auch keine Ausgabe der Zeile Console.WriteLine(...)
Also liegt die Vermutung nahe das die Methode gar nicht aufgerufen wird. Beim expliziten Methodenaufruf kommt auch die Ausgabe ...

Zitat:
Solange du also nur andere CLR-Klassen wie den StreamWriter benutzt, brauchst du überhaupt keinen Destruktor.


Ergo: Wenn ich einfach nix tue (Kein Implementierung eines Destruktors / Interfaces (IDisposable)) dann ist alles ok? Habe ich ausprobiert es funktioniert .. wie von Geisterhand ....
Eine komische Programiersprache ... :shock:
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: So 26.04.09 20:41 
user profile iconFrankyBoy69 hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings kommt auch keine Ausgabe der Zeile Console.WriteLine(...)
Wie gesagt ist IDisposable nur ein Pattern, die CLR weiß nichts davon. Du musst Dispose selbst aufrufen oder einen using-Block benutzen.
Zitat:
Ergo: Wenn ich einfach nix tue (Kein Implementierung eines Destruktors / Interfaces (IDisposable)) dann ist alles ok? Habe ich ausprobiert es funktioniert .. wie von Geisterhand ....
Wobei es beim StreamWriter Probleme geben kann: Der Stream könnte vor dem Writer freigegeben werden, dann sind die noch im Puffer liegenden Daten futsch. Ein IDisposable, das den StreamWriter dispost, brauchst du also auf jeden Fall.
Zitat:
Eine komische Programiersprache ... :shock:
Da wohl 90% aller modernen Sprachen einen Garbage Collector verwenden, sind wir nicht in schlechter Gesellschaft ;) .

_________________
>λ=