33

我显然已经养成了一个不好的编码习惯。这是我一直在编写的代码示例:

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()and Dispose()on theStreamReaderFileStream将被关闭。

我可以解决我遇到的问题的唯一方法是将上面的块更改为:

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

5 回答 5

41

是的,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.CreateFileMode.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");
    }
}
于 2009-04-01T21:06:42.770 回答
12

注意 - 您的 using 块不需要嵌套在它们自己的块中 - 它们可以是顺序的,如:

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

这种情况下的处理顺序仍然与嵌套块相同(即,在这种情况下,StreamReader 仍将在 FileStream 之前进行处理)。

于 2009-04-01T21:40:11.970 回答
1

我会尝试使用FileInfo.Open()andFileInfo.MoveTo()而不是File.Open()and File.Move()。您也可以尝试使用FileInfo.OpenText(). 但这些只是建议。

于 2009-04-01T21:53:35.043 回答
0

有没有可能其他东西锁定了 somefile.txt?

从本地(到文件)cmd 行的简单检查

net files

如果其他东西有锁,可能会给你一些线索。

或者,您可以使用FileMon 之类的工具获取更多详细信息,并检查您的应用程序是否正确发布。

于 2009-04-01T21:41:29.830 回答
0

由于这似乎不是编码问题,因此我将戴上我的 syadmin 帽子并提供一些建议。

  1. 在创建文件时扫描文件的客户端或服务器上的病毒扫描程序。
  2. Windows机会主义锁定习惯于在网络共享上搞砸事情。我记得这主要是具有平面文件数据库的多个读/写客户端的问题,但是缓存肯定可以解释您的问题。
  3. Windows file open cache. I'm not sure if this is still a problem in Win2K or not, but FileMon would tell you.

Edit: If you can catch it in the act from the server machine, then Sysinternal's Handle will tell you what has it open.

于 2009-04-03T00:00:56.500 回答