如何快速检查,如果文件夹为空(.NET)?为空、文件夹、快速、NET

2023-09-02 21:10:04 作者:她又失约了

我要检查,如果磁盘目录是空的。这意味着,它不包含任何文件夹/文件。我知道,有一个简单的方法。我们得到FileSystemInfo的阵列,并检查是否元素的数量等于零。类似的东西:

I have to check, if directory on disk is empty. It means, that it does not contain any folders/files. I know, that there is a simple method. We get array of FileSystemInfo's and check if count of elements equals to zero. Something like that:

    public static bool CheckFolderEmpty(string path)
    {
        if (string.IsNullOrEmpty(path))
        {
            throw new ArgumentNullException("path");
        }

        var folder = new DirectoryInfo(path);
        if (folder.Exists)
        {
            return folder.GetFileSystemInfos().Length == 0;
        }

        throw new DirectoryNotFoundException();
    }

这个方法似乎确定。但!!这是非常,非常糟糕的表现透视。 GetFileSystemInfos()是一个非常困难的方法。事实上,它枚举文件夹的所有文件系统对象,获取所有属性,创建对象,填充类型数组等,而这一切都只是简单地检查长度。这是愚蠢的,不是吗?

This approach seems OK. BUT!! It is very, very bad from a perspective of performance. GetFileSystemInfos() is a very hard method. Actually, it enumerates all filesystem objects of folder, gets all their properties, creates objects, fills typed array etc. And all this just to simply check Length. That's stupid, isn't it?

我只是异形等code和决心,这样的方法〜250电话在〜500ms的执行。这是非常慢的,我相信,这是可以做到这快得多。

I just profiled such code and determined, that ~250 calls of such method are executed in ~500ms. This is very slow and I believe, that it is possible to do it much quicker.

有什么建议?

推荐答案

下面是额外的快速解决,我终于实现。在这里,我使用WinAPI的和函数的用FindFirstFile 的 FindNextFile 的。它允许以避免在文件夹中的所有项目枚举和停在文件夹中检测到的第一个对象之后。这种方法比较快〜6(!!)次,比上述。在36ms 250电话!

Here is the extra fast solution, that I finally implemented. Here I am using WinAPI and functions FindFirstFile, FindNextFile. It allows to avoid enumeration of all items in Folder and stops right after detecting the first object in the Folder. This approach is ~6(!!) times faster, than described above. 250 calls in 36ms!

private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct WIN32_FIND_DATA
{
    public uint dwFileAttributes;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
    public uint nFileSizeHigh;
    public uint nFileSizeLow;
    public uint dwReserved0;
    public uint dwReserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string cFileName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
    public string cAlternateFileName;
}

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

[DllImport("kernel32.dll")]
private static extern bool FindClose(IntPtr hFindFile);

public static bool CheckDirectoryEmpty_Fast(string path)
{
    if (string.IsNullOrEmpty(path))
    {
        throw new ArgumentNullException(path);
    }

    if (Directory.Exists(path))
    {
        if (path.EndsWith(Path.DirectorySeparatorChar.ToString()))
            path += "*";
        else
            path += Path.DirectorySeparatorChar + "*";

        WIN32_FIND_DATA findData;
        var findHandle = FindFirstFile(path, out findData);

        if (findHandle != INVALID_HANDLE_VALUE)
        {
            try
            {
                bool empty = true;
                do
                {
                    if (findData.cFileName != "." && findData.cFileName != "..")
                        empty = false;
                } while (empty && FindNextFile(findHandle, out findData));

                return empty;
            }
            finally
            {
                FindClose(findHandle);
            }
        }

        throw new Exception("Failed to get directory first file",
            Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
    }
    throw new DirectoryNotFoundException();
}

我希望这将是某人在未来有用。

I hope it will be useful for somebody in the future.