6

If I have this:

StreamWriter cout = new StreamWriter("test.txt");
cout.WriteLine("XXX");

// here IOException...
StreamReader cin = new StreamReader("test.txt");
string text = cin.ReadLine();

the clr throws an IOException because I haven't close the cout.

In fact If I do this:

StreamWriter cout = new StreamWriter("test.txt");
cout.WriteLine("XXX");

cout.Close();

StreamReader cin = new StreamReader("test.txt");
string text = cin.ReadLine();

I have no exception.

But If I do this and then exit from the application:

StreamReader cin = new StreamReader("test.txt");
string text = cin.ReadLine();

without closing cin the file can from the OS opened and written.

However reading the source code of StreamReader.cs I didn't' find a destructor method (i.e. ~StreamReader(...)). So who does free that file if the garbage collector doesn't invoke Dispose and there is no finalization method?

Liam
  • 27,717
  • 28
  • 128
  • 190
xdevel2000
  • 20,780
  • 41
  • 129
  • 196
  • Possible duplicate of [Should I call Close() or Dispose() for stream objects?](http://stackoverflow.com/questions/7524903/should-i-call-close-or-dispose-for-stream-objects) – Meirion Hughes Apr 28 '16 at 10:30
  • 1
    StreamReader creates a FileStream with FileShare.Read That denies anybody from writing to the file, gives you the guarantee that the file won't change while you are reading it. Nice. But that can't work, the StreamWriter asked for FileAccess.Write and got it. So you'll be denied access instead since the OS can no longer guarantee that the file won't change. If you want to throw that guarantee away then use FileStream to open the file with FileShare.ReadWrite and pass it to the StreamReader constructor. – Hans Passant Apr 28 '16 at 10:39

4 Answers4

5

Internally, the StreamReader uses a FileStream:

 Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);

The FileStream class, being the class that ultimately accesses the file and therefore needs to guarantee cleanup, does have a finalizer, which closes the actual underlying stream. The Dispose method on the StreamReader just calls the Close on the underlying FileStream.

James Thorpe
  • 31,411
  • 5
  • 72
  • 93
3

StreamReader and StreamWriter uses a FileStream to access the file. FileStream uses SafeFileHandle to store the underlying OS file handle. As the SafeFileHandle class controls an unmanaged resource it correctly has a finalizer (what you call a destructor) that closes the file handle.

But If I do this and then exit from the application: [...] without closing cin the file can from the OS opened and written

When a process terminates all resources used by that process is released to the operating system. It doesn't matter if your application forgot to close a file handle (even though SafeFileHandle will not "forget"). You will always observe the described behavior no matter how badly written your application is.

I just want to point out that the best way to work with StreamReader and StreamWriter and similar classes is using:

using (StreamWriter cout = new StreamWriter("test.txt")) {
  cout.WriteLine("XXX");
}

using (StreamReader cin = new StreamReader("test.txt")) {
  string text = cin.ReadLine();
}

This deterministically closes the files when the using block ends even if an exception is thrown while processing the file.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
0

The operating system has a list which process (application) has what file opened. If your process terminates without explicitly closing the file, the operating system still knows that it is not any longer accessing the file and so can allow other requests to access the file.

NineBerry
  • 26,306
  • 3
  • 62
  • 93
0

Operation sytem frees handles and memory owned by application, if the were not freed by application if it closes. Anyway, I'm sure Stream has finalizer.

Qwertiy
  • 19,681
  • 15
  • 61
  • 128