In this article, we are going to see how to get the number of files in a folder. C# provides several methods to determine file counts within a folder, each tailored for specific use cases. 

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

Use the Directory.GetFiles Method to Get the Number of Files in a Folder

To keep things simple, we’ll focus on using the Directory class for this section of the article. But feel free to explore the differences and usage of the Directory and DirectoryInfo classes.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Now for our basic approach, we’ll examine using Directory.GetFiles(), a common method for retrieving file paths within a specified directory:

static int CountFilesUsingGetFiles(string directoryPath)
{
    return Directory.GetFiles(directoryPath).Length;
}

We retrieve an array of file paths within the directoryPath and determine the count by accessing the array’s Length property.

It’s crucial to note that this method offers flexibility through search options. It offers the TopDirectoryOnly option that confines the search to files residing directly within the specified directory, excluding files within subdirectories. Also, it offers the AllDirectories option that expand the search scope to search for files within the primary directory as well as its subdirectories.

Wildcard Usage and Performance Considerations

The Directory.GetFiles() method supports wildcards for selective file retrieval. For instance, Directory.GetFiles(directoryPath, "*.txt", SearchOption.AllDirectories) specifically targets text files within the primary directory and subdirectories.

However, we have to be careful when using wildcards, as they can potentially impact performance, especially when dealing with directories containing a lot of files.

Despite the simplicity of this approach, it is crucial to recognize the implications for performance. It can take a long time to retrieve all file paths into an array, especially when working with directories that contain numerous files. This can double memory usage and cause the file-counting process to run much slower.

This leads us to explore other alternative methods that address this aspect.

Use LINQ With Directory.EnumerateFiles to Get the Number of Files in a Folder

This approach combines LINQ’s versatile query capabilities with memory-efficient enumeration, allowing us to precisely count files while preserving memory resources in situations where memory optimization is crucial.

Let’s see how this approach works using a code sample:

static int CountFilesUsingLINQEnumerateFiles(string directoryPath)
{
    return Directory
        .EnumerateFiles(directoryPath)
        .Count();
}

Here, we use the Directory.EnumerateFiles() method with the Count() method to count the number of files in the directoryPath.

The key advantage of this approach lies in its iterative nature. It builds file paths as needed, rather than loading them all in memory at once. This can result in better performance and less memory usage, particularly when reading directories with lots of files.

Similar to our previous method, this method also allows for the use of wildcards and provides flexibility with search options.

Use the WinAPI to Get the Number of Files in a Folder

For experienced developers looking for the best possible performance optimization when trying to get file count in a folder, the WinAPI (Windows Application Programming Interface) offers a powerful, although intricate, approach. It provides direct access to system resources at a low level, potentially going beyond the bounds of managed code techniques.

Although using WinAPI can be quite beneficial, it does require extensive understanding and comfort with platform-specific nuances. Let’s see how to use this approach.

First, we’ll start by importing the necessary namespace:

using System.Runtime.InteropServices;

Next, we’ll declare the WinAPI function from the kernel32.dll:

[DllImport("kernel32.dll")]
static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]

Declaring these functions gives us fine-grained control over file enumeration processes by directly connecting our code to the Windows API’s file system capabilities.

The [DllImport("kernel32.dll")] attribute, applied to static extern methods, instructs the C# compiler to locate the specified functions within kernel32.dll and enable their usage within the code.

Within the specified directory, the FindFirstFile initiates file enumeration and returns a “handle” that serves as a point of reference for further actions. Next, using the handle we get from FindFirstFile, FindNextFile obtains details about the next file in the enumeration sequence. Ultimately FindClose releases handle-related resources, guaranteeing correct cleanup and guarding against memory leaks.

The next step is to define the WIN32_FIND_DATA Structure:

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

This structure serves as a blueprint, that would help to provide detailed information on every file that is encountered throughout the enumeration process. It acts as a link between our code and the internal file representation of the Windows API.

Now, let’s implement the file-counting method:

static int CountFilesUsingWinAPI(string directoryPath, bool searchSubDirectories = false)
{
    if (!Directory.Exists(directoryPath))
        throw new DirectoryNotFoundException(directoryPath);

    var searchPattern = Path.Combine(directoryPath, "*");
    var findData = new WIN32_FIND_DATA();

    var handleFindFile = FindFirstFile(searchPattern, out findData);

    var fileCount = 0;

    if (handleFindFile != IntPtr.Zero)
    {
        do
        {
            if ((findData.dwFileAttributes & FileAttributes.Directory) == 0)
            {
                fileCount++;
            }
            if (searchSubDirectories
                && findData.cFileName != "..")
            {
                var subDir = Path.Combine(directoryPath, findData.cFileName);
                fileCount += CountFilesUsingWinAPI(subDir, false);
            }
        } while (FindNextFile(handleFindFile, out findData));

        FindClose(handleFindFile);
    }

    return fileCount;
}

This method directly interacts with the Windows API to determine the number of non-directory, non-hidden files within a specified directory. Finally, it returns the total number of files that satisfy the specified criteria.

Platform Dependency and Complexity Disclaimer

It is important to note that WinAPI is not compatible with other operating systems, it is exclusively designed for the Windows platform. Because of its low-level nature and need for in-depth knowledge of system internals, it is considered best suited for experienced developers.

In conclusion, WinAPI offers significant potential for optimizing file counting efficiency. However, because of its platform dependency and complexity, much thought should be given before using it. For most scenarios, the built-in functions like Directory.EnumerateFiles with LINQ offers a well-balanced combination of performance and ease of use.

Performance Comparison

We’ve looked at several file-counting techniques, each with their strength and peculiarity. Now let’s use benchmarks to examine their performance.

We won’t be looking for files in subdirectories or using any filters. Instead, we’ll compare the approaches to see which is quickest for counting files inside a single directory: 

|                                Method | FileCount |        Mean  |   Allocated |
|-------------------------------------- |----------:|-------------:|------------:|
|      DirectoryEnumerateFiles_InMemory |      1000 |     915.8 us |   133.03 KB |
|                  WinAPICount_InMemory |      1000 |   1,283.2 us |    46.96 KB |
|            DirectoryGetFiles_InMemory |      1000 |   1,308.7 us |   157.08 KB |
|      DirectoryEnumerateFiles_InMemory |      5000 |   4,891.6 us |   664.28 KB |
|                  WinAPICount_InMemory |      5000 |   5,005.2 us |   234.46 KB |
|            DirectoryGetFiles_InMemory |      5000 |   6,688.2 us |   831.65 KB |
|      DirectoryEnumerateFiles_InMemory |     10000 |   8,078.4 us |  1328.35 KB |
|                  WinAPICount_InMemory |     10000 |  11,226.9 us |   468.84 KB |
|            DirectoryGetFiles_InMemory |     10000 |  11,846.1 us |  1662.83 KB |

According to our benchmark results, the CountFilesUsingLINQEnumerateFiles() method offers a marginally faster raw performance for counting files in small-sized folders and folders containing many files, making it the fastest approach.

However, the CountFilesUsingWinAPI() method uses the least amount of memory and also performs well. Finally, despite its best efforts, the CountFilesUsingGetFiles() method consistently performs somewhat worse than the other two options and requires a larger memory allocation.

Practical Considerations

Despite the seemingly simple nature of file counting using C#, real-world situations might present unexpected challenges. It is important to consider potential pitfalls and handle them to guarantee a clean and solid code.

Imagine our program attempting to count files in a directory that doesn’t exist, this would trigger an exception and crash the application. To avoid these kinds of situations, we can use the try-catch exception handling technique.

We can catch any potential exceptions thrown within our try block, such as the common FileNotFoundException, DirectoryNotFoundException, or UnauthorizedAccessException errors. After that, the catch block can give us a personalized message explaining the problem and the particular directory that is affected.

Conclusion

In this article, we saw several methods used for counting the number of files in a folder and saw the strengths and considerations for each approach. To find out which method was fastest, we also ran benchmarks for each of them.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!