Будет ли предложение использования закрывать этот поток?
Я, видимо, работал сам с плохой привычкой кодирования. Вот пример кода, который я писал:
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
Я думал, что, поскольку предложение using
, явно называемое Close()
и Dispose()
на StreamReader
, также будет закрыто FileStream
.
Единственный способ, которым я мог исправить проблему, которую я имел, заключался в том, что вы изменили этот блок:
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
Если закрытие StreamReader
путем размещения в первом блоке также закрывает базовый FileStream
? Или я ошибался?
Изменить
Я решил опубликовать фактический оскорбительный блок кода, чтобы посмотреть, можем ли мы разобраться в этом. Сейчас мне просто интересно.
Я думал, что у меня проблема в предложении using
, поэтому я все расширил, и он все равно не может копировать каждый раз. Я создаю файл в этом вызове метода, поэтому я не думаю, что что-то еще имеет дескриптор, открытый в файле. Я также проверил, что строки, возвращенные из вызовов Path.Combine
, верны.
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);
}
Ответы
Ответ 1
Да, StreamReader.Dispose
закрывает базовый поток (для всех общедоступных способов его создания). Однако есть более приятная альтернатива:
using (TextReader reader = File.OpenText("file.txt"))
{
}
Это дает дополнительное преимущество: он открывает базовый поток с подсказкой в Windows, к которой вы будете обращаться последовательно.
Здесь тестовое приложение, в котором показана первая версия, работающая для меня. Я не пытаюсь сказать это доказательство чего-либо в частности - но мне бы хотелось узнать, как хорошо он работает для вас.
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");
}
}
}
Если это сработает, это говорит о том, что это связано с тем, что вы делаете во время чтения...
И вот теперь укороченная версия вашего отредактированного кода вопроса, которая снова отлично работает для меня, даже на сетевом ресурсе. Обратите внимание, что я изменил FileMode.Create
на FileMode.CreateNew
- так как в противном случае, возможно, все еще могло быть приложение с дескриптором старого файла. Это работает для вас?
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");
}
}
Ответ 2
Примечание. Ваши блоки использования не обязательно должны быть вложены в их собственные блоки - они могут быть последовательными, как в:
using(FileStream fs = File.Open("somefile.txt", FileMode.Open))
using(StreamReader sr = new StreamReader(fs))
{
//read file
}
Порядок удаления в этом случае по-прежнему совпадает с вложенными блоками (т.е. StreamReader все равно будет располагаться до FileStream в этом случае).
Ответ 3
Я попытался бы использовать FileInfo.Open()
и FileInfo.MoveTo()
вместо File.Open()
и File.Move(
). Вы также можете попробовать использовать FileInfo.OpenText()
. Но это только предложения.
Ответ 4
Есть ли вероятность, что что-то еще имеет блокировку для файла somefile.txt?
Простая проверка из локальной (в файл) строки cmd
net files
может дать вам некоторые подсказки, если что-то еще заблокировано.
В качестве альтернативы вы можете получить что-то вроде FileMon, чтобы получить еще более подробную информацию, и убедитесь, что ваше приложение правильно освобождается.
Ответ 5
Поскольку это не похоже на проблему с кодировкой, я собираюсь включить шляпу syadmin и предложить несколько предложений.
- Сканер вирусов на клиенте или сервере, который сканирует файл по мере его создания.
- Windows оппортунистическая блокировка имеет привычку зависеть от сетевых ресурсов. Я помню, что это в основном проблема с несколькими клиентами чтения/записи с плоскими файловыми базами, но кэширование могло бы объяснить вашу проблему.
- Windows открыть файл кэша. Я не уверен, что это все еще проблема в Win2K или нет, но FileMon расскажет вам.
Изменить: если вы можете поймать его в действии с сервера, тогда Sysinternal Handle сообщит вам, что он открыл.