取消

.NET/Windows:删除文件夹后立即判断,有可能依然存在

如果你不了解本文的内容,可能会在未来某个时候踩坑–你可能在判断文件夹是否存在的时候得到错误的返回值。


删除文件(夹)

使用 .NET 带的删除文件夹的方法:

1
Directory.Delete("D:\walterlv");

或者使用其他删除文件(夹)的方法,大多数是以下 Windows API 的封装:

1
2
3
4
5
6
7
8
9
BOOL DeleteFile(
  LPCTSTR lpFileName
);
BOOL RemoveDirectoryA(
  LPCSTR lpPathName
);
BOOL RemoveDirectoryW(
  LPCWSTR lpPathName
);

于是,大多数删除文件(夹)的代码都会遇到问题:文件或文件夹可能没有立即删除

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        var dirPath = @"C:\Users\lvyi\Desktop\Test";
        while (true)
        {
            Console.WriteLine(Directory.Exists(dirPath));
            Directory.CreateDirectory(dirPath);
            Directory.Delete(dirPath);
        }
    }
}

你猜会输出什么?每一行都输出 False 吗?

然而实际上是:

输出

嗯……会混入少量的 True 在里面。这是不是有点“不符合预期”?

原因

删除文件夹的本质方法 RemoveDirectory 在微软的官网中就已经解释了:

The RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.

RemoveDirectory 函数将标记一个文件夹在关闭后删除。这意味着在最后一个此文件夹的句柄关闭之前,此文件夹将一直不会删除。

所以调用完删除文件夹的方法后,仅仅只是标记这个文件夹要删除而已。那么随后立即获取此文件夹是否存在,将取决于前面调用删除后是否真的删除了文件夹。

删除文件的本质方法 DeleteFile 也有类似的解释:

The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.

DeleteFile 函数将标记一个文件在关闭后删除。这意味着在最后一个文件句柄关闭之前,此文件将一直不会删除。如果随后立即调用 CreateFile 来打开一个文件的话可能会遭遇错误 ERROR_ACCESS_DENIED

解决方法

因此,不要再依赖于判断文件夹是否存在来决定某个业务。例如,可以考虑创建文件夹之前不判断文件夹是否存在:

1
2
3
4
--  if (Directory.Exists(path))
--  {
        Directory.CreateDirectory(path);
--  }

注意,以上红色色块标记的代码应该删除!否则你可能会发现这段代码执行完成后,文件夹是不存在的。

如果试图删除文件随后新建空白的文件或者其他文件的话,可以考虑我在另一篇博客中提到的创建或打开文件的方法,用来应对文件不存在的情况:

发现 dotnet 职业技术学院另一小伙伴也遇到这个问题:


参考资料

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/file-or-directory-delete-is-not-completed-after-calling-delete.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

登录 GitHub 账号进行评论