Will a using clause close this stream?
Solution 1
Yes, StreamReader.Dispose
closes the underlying stream (for all public ways of creating one). However, there's a nicer alternative:
using (TextReader reader = File.OpenText("file.txt"))
{
}
This has the added benefit that it opens the underlying stream with a hint to Windows that you'll be accessing it sequentially.
Here's a test app which shows the first version working for me. I'm not trying to say that's proof of anything in particular - but I'd love to know how well it works for you.
using System;
using System.IO;
class Program
{
public static void Main(string[] args)
{
for (int i=0; i < 1000; i++)
{
using(StreamReader sr = new StreamReader
(File.Open("somefile.txt", FileMode.Open)))
{
Console.WriteLine(sr.ReadLine());
}
File.Move("somefile.txt", "somefile.bak");
File.Move("somefile.bak", "somefile.txt");
}
}
}
If that works, it suggests that it's something to do with what you do while reading...
And now here's a shortened version of your edited question code - which again works fine for me, even on a network share. Note that I've changed FileMode.Create
to FileMode.CreateNew
- as otherwise there could still have been an app with a handle on the old file, potentially. Does this work for you?
using System;
using System.IO;
public class Test
{
static void Main()
{
StreamWriter creditsFile = new StreamWriter(File.Open("test.txt",
FileMode.CreateNew));
creditsFile.WriteLine("code\\inc");
creditsFile.Close();
creditsFile.Dispose();
File.Move("test.txt", "test2.txt");
}
}
Solution 2
Note - your using blocks do not need to be nested in their own blocks - they can be sequential, as in:
using(FileStream fs = File.Open("somefile.txt", FileMode.Open))
using(StreamReader sr = new StreamReader(fs))
{
//read file
}
The order of disposal in this case is still the same as the nested blocks (ie, the StreamReader will still dispose before the FileStream in this case).
Solution 3
I would try to use FileInfo.Open()
and FileInfo.MoveTo()
instead of File.Open()
and File.Move(
). You could also try to use FileInfo.OpenText()
. But these are just suggestions.
scottm
Software engineer with skills in highly available systems and software solutions.
Updated on March 18, 2020Comments
-
scottm about 4 years
I've apparently worked myself into a bad coding habit. Here is an example of the code I've been writing:
using(StreamReader sr = new StreamReader(File.Open("somefile.txt", FileMode.Open))) { //read file } File.Move("somefile.txt", "somefile.bak"); //can't move, get exception that I the file is open
I thought that because the
using
clause explicitly calledClose()
andDispose()
on theStreamReader
that theFileStream
would be closed as well.The only way I could fix the problem I was having was by changing the above block to this:
using(FileStream fs = File.Open("somefile.txt", FileMode.Open)) { using(StreamReader sr = new StreamReader(fs)) { //read file } } File.Move("somefile.txt", "somefile.bak"); // can move file with no errors
Should closing the
StreamReader
by disposing in the first block also close the underlyingFileStream
? Or, was I mistaken?Edit
I decided to post the actual offending block of code, to see if we can get to the bottom of this. I am just curious now.
I thought I had a problem in the
using
clause, so I expanded everything out, and it still can't copy, every time. I create the file in this method call, so I don't think anything else has a handle open on the file. I've also verified that the strings returned from thePath.Combine
calls are correct.private static void GenerateFiles(List<Credit> credits) { Account i; string creditFile = Path.Combine(Settings.CreditLocalPath, DateTime.Now.ToString("MMddyy-hhmmss") + ".credits"); StreamWriter creditsFile = new StreamWriter(File.Open(creditFile, FileMode.Create)); creditsFile.WriteLine("code\inc"); foreach (Credit c in credits) { if (DataAccessLayer.AccountExists(i)) { string tpsAuth = DataAccessLayer.GetAuthCode(i.Pin); creditsFile.WriteLine(String.Format("{0}{1}\t{2:0.00}", i.AuthCode, i.Pin, c.CreditAmount)); } else { c.Error = true; c.ErrorMessage = "NO ACCOUNT"; } DataAccessLayer.AddCredit(c); } creditsFile.Close(); creditsFile.Dispose(); string dest = Path.Combine(Settings.CreditArchivePath, Path.GetFileName(creditFile)); File.Move(creditFile,dest); //File.Delete(errorFile); }