Будет ли условие использования закрывать этот поток? - PullRequest
26 голосов
/ 02 апреля 2009

Я, очевидно, выработал у меня плохую привычку кодирования. Вот пример кода, который я писал:

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);
}

Ответы [ 5 ]

37 голосов
/ 02 апреля 2009

Да, 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");
    }
}
11 голосов
/ 02 апреля 2009

Примечание. Используемые вами блоки не обязательно должны быть вложены в их собственные блоки - они могут быть последовательными, как в:

using(FileStream fs = File.Open("somefile.txt", FileMode.Open))
using(StreamReader sr = new StreamReader(fs))
{
    //read file
}

Порядок удаления в этом случае все тот же, что и для вложенных блоков (т. Е. StreamReader все равно будет располагаться перед FileStream в этом случае).

1 голос
/ 02 апреля 2009

Я бы попытался использовать FileInfo.Open() и FileInfo.MoveTo() вместо File.Open() и File.Move(). Вы также можете попробовать использовать FileInfo.OpenText(). Но это всего лишь предложения.

0 голосов
/ 03 апреля 2009

Поскольку это, похоже, не проблема кодирования, я надену шляпу сядмина и предложу несколько предложений.

  1. Сканер вирусов на клиенте или сервере, который сканирует файл при его создании.
  2. Windows оппортунистическая блокировка имеет привычку портить сетевые ресурсы. Я помню, что это в основном проблема с несколькими клиентами для чтения / записи с базами данных плоских файлов, но кэширование , безусловно, может объяснить вашу проблему.
  3. Windows кэш открытия файлов . Я не уверен, если это все еще проблема в Win2K или нет, но FileMon скажет вам.

Редактировать: Если вы можете поймать его в действии с сервера, то дескриптор Sysinternal скажет вам, что он открыт.

0 голосов
/ 02 апреля 2009

Есть ли вероятность того, что что-то еще имеет блокировку для somefile.txt?

Простая проверка из локальной (в файл) строки cmd

net files

вполне может дать вам некоторые подсказки, если у чего-то еще есть замок.

В качестве альтернативы вы можете получить что-то вроде FileMon , чтобы получить еще больше деталей и проверить, правильно ли выходит ваше приложение.

...