File.Exists 在 File.Delete 后返回 true

Pes*_*ter 5 c# file-io

我有以下方法来删除具有提供路径的文件

private void DestroyFile(string path)
{
    try
    {
        if (File.Exists(path))
        {
            File.Delete(path);
        }
        if (File.Exists(path))
        {
            throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果文件存在于 File.Delete 方法之后,我将收到抛出的 IOException。具体来说

System.IO.IOException): 无法删除文件: 'C:\Windows\TEMP\[FILE NAME]'。

我也确认在执行完成后在path变量中的位置不存在该文件。我想知道我是否在 File.Delete 之后更新文件系统和使用 File.Exists 再次检查它之间遇到竞争条件。有没有更好的方法平滑删除?我知道如果文件不存在, File.Delete 不会返回错误,所以这些检查可能有点多余。我应该检查文件是否在使用中而不是它是否存在?

一些重要的附加信息: 该程序可以并且确实经常成功运行,但最近经常看到此特定错误。

Evk*_*Evk 7

File.Delete标记要删除的文件。只有当文件的所有句柄都关闭时,文件才会真正被删除(如果没有这样的句柄 - 它总是在 File.Delete 返回后被删除)。如DeleteFile winapi 函数(由 C# 使用File.Delete)所述:

DeleteFile 函数在关闭时标记要删除的文件。因此,在关闭文件的最后一个句柄之前不会发生文件删除

通常,您删除的文件没有打开的句柄。或者,如果有打开的句柄 - 它们通常没有“删除”共享(此共享允许另一个进程将文件标记为删除),因此当您尝试删除此类文件时 - 它要么被删除(没有打开的句柄)要么访问被拒绝或类似的异常被抛出(一些句柄,但没有删除共享)。

但是,有时某些软件(例如防病毒软件或搜索索引器)可能会使用“删除”共享打开任意文件并将它们保留一段时间。如果您尝试删除此类文件 - 它不会出错,并且当该软件关闭其句柄时,文件确实会被删除。但是,File.Exists对于此类“待删除”文件,将返回 true。

你可以用这个简单的程序重现这个问题:

public class Program {
    public static void Main() {
        string path = @"G:\tmp\so\tmp.file";
        // create file with delete share and don't close handle
        var file = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete);
        DestroyFile(path);
        GC.KeepAlive(file);
    }

    private static void DestroyFile(string path) {
        try {

            if (File.Exists(path)) {
                // no error
                File.Delete(path);
            }
            // but still exists
            if (File.Exists(path)) {
                throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
            }
        }
        catch (Exception ex) {
            throw ex;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以File.Exists在上面的程序中永远重试检查 - 文件将一直存在,直到您关闭句柄。

所以这就是你的情况 - 一些程序打开了这个文件的句柄FileShare.Delete.

你应该预料到这种情况。例如 - 只需删除该File.Exists检查,因为您将文件标记为删除,无论如何它都会被删除。