题 using子句会关闭此流吗?


我显然已经习惯了一个糟糕的编码习惯。以下是我编写的代码示例:

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

23
2018-04-01 21:05


起源


某些其他应用程序是否可以打开并锁定该文件?您可能也想检查一下。 - Daniel
DateTime.Now.ToString("MMddyy-hhmmss")  您每秒创建多少个文件? - Dave
@Dave,一天一个。此功能每天只调用一次。 - scottm
检查防病毒扫描程序,索引服务器等。 - Dominic Cronin


答案:


是, StreamReader.Dispose 关闭基础流(用于创建一个的所有公共方式)。但是,有一个更好的选择:

using (TextReader reader = File.OpenText("file.txt"))
{
}

这有一个额外的好处,它打开底层流,提示您将按顺序访问它。

这是一个测试应用程序,显示第一个版本适合我。我并不想说这是任何特别的证据 - 但我很想知道它对你有多好。

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

35
2018-04-01 21:06



知道为什么我不能在第一个块中移动文件吗? - scottm
不 - 那应该没问题。奇。 - Jon Skeet
可以 总是 第一个版本失败了 总是 与第二个一起工作? - Jon Skeet
@jon,是的。有规律的。 - scottm
刚尝试了第一个版本,它在循环中为我工作了20次。 - Jon Skeet


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

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

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


10
2018-04-01 21:40



对我来说,它看起来更好。 - scottm
理解这与任何其他具有initial子句的构造没有什么不同,后面跟一个语句,其中语句可以是一个块。 (一个典型的例子是 if (list != null) foreach (object item in list) { ... },这是一个 foreach 嵌套在里面 if。)具体来说,这个 是 “嵌套” - 以及非理想的缩进样式,因为它不表示嵌套。如果你缩进除第一行以外的所有行,则更清楚的是:第二行“using”嵌套在第一行中。 - ToolmakerSteve


我会尝试使用 FileInfo.Open() 和 FileInfo.MoveTo() 代替 File.Open() 和 File.Move()。你也可以尝试使用 FileInfo.OpenText()。但这些只是建议。


1
2018-04-01 21:53



我将尝试使用FileInfo方法,但我认为在下面,它们只是对File.Move的调用 - scottm
是的,但也许他们在内部处理流(由FileInfo.Open()创建)。此外,文档说他们只做了一次安全检查,所以它可能会稍快一点...... - MartinStettner


有没有其他东西可以锁定somefile.txt的可能性?

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

net files

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

或者你可以得到类似的东西 FileMon的 获取更多详细信息,并检查您的应用是否正确发布。


0
2018-04-01 21:41





由于这似乎不是一个编码问题,我将打开我的syadmin帽子,并提出一些建议。

  1. 在创建文件时扫描文件的客户端或服务器上的病毒扫描程序。
  2. 视窗 机会锁定 有习惯搞砸网络股票。我记得这主要是多个具有平面文件数据库的读/写客户端的问题,但是 高速缓存 当然可以解释你的问题。
  3. 视窗 文件打开缓存。我不确定这是否仍然是Win2K中的问题,但FileMon会告诉你。

编辑:如果您可以从服务器机器中捕获它,那么Sysinternal的Handle会告诉您它打开了什么。


0
2018-04-03 00:00



很有意思。我在过去的另一个应用程序中也遇到了问题,我没有编码,但我们发现了罪魁祸首是AVG。我要调查一下。 - scottm