用 Directory.GetFiles 最快最准统计文件数,因返回 string[] 可直接用 .Length 获取;递归需显式加 SearchOption.AllDirectories,但权限不足会抛异常而非返回0。

用 Directory.GetFiles 最快也最准
统计文件数量,别碰 Directory.EnumerateFiles 再自己 .Count() —— 它会遍历完所有路径才计数,内存不占多但纯属多绕一步。而 Directory.GetFiles 返回的是 string[],.Length 直接拿结果,底层是 Win32 FindFirstFile 系列调用,快且稳定。
常见错误是传错搜索选项:默认只查一级目录,想递归得加 SearchOption.AllDirectories;但要注意,如果目录极深或权限不足,这里会直接抛 UnauthorizedAccessException 或 IOException,不是返回 0。
-
Directory.GetFiles(@"C:Logs")→ 只统计C:Logs下的文件(不含子目录) -
Directory.GetFiles(@"C:Logs", "*.*", SearchOption.AllDirectories)→ 全递归,包括所有子目录里的文件 - 想排除某些类型?用第二个参数做模式匹配,比如
"*.log"或"config?.json"(支持简单通配符)
需要过滤或预处理时,才用 Directory.EnumerateFiles
如果你不只是数总数,还要判断文件大小、创建时间、扩展名,或者中途想 break 掉(比如找到第 100 个就停),那 EnumerateFiles 才合适——它返回 IEnumerable<string></string>,延迟执行,边枚举边处理。
但注意:一旦你对它调用 .Count(),就等于强制枚举全部,性能反而不如 GetFiles;真要计数+过滤,写成 EnumerateFiles(...).Where(...).Count() 是典型反模式,既没省内存,又丢了速度。
- 正确姿势:
int count = Directory.EnumerateFiles(path, "*.tmp").Count();—— 如果你确定过滤后数量不会爆炸,且能接受全量扫描 - 更稳的写法:
int count = 0; foreach (var f in Directory.EnumerateFiles(path, "*.tmp")) count++;—— 避免 LINQ 的额外开销和异常包装 - Windows 上路径含中文、特殊符号完全没问题,但 Linux/macOS 运行时(.NET Core/.NET 5+)要注意大小写敏感问题
统计失败的三个高频原因
不是代码写错,而是环境或权限卡住。报错信息往往很直白,但容易被忽略:
-
UnauthorizedAccessException:目标目录有子目录无读取权限(比如系统还原点、$RECYCLE.BIN),GetFiles直接崩;EnumerateFiles在foreach里才崩,可 try/catch 单个路径 -
DirectoryNotFoundException:路径字符串末尾多了斜杠(如"C:\Temp\"),某些旧版 .NET 对双反斜杠或结尾斜杠敏感,统一用Path.Combine拼接 - 空目录返回 0 没问题,但若期望非零却得到 0,请检查搜索模式是否写成了
"*"(不合法)而不是"*.*"或"*"(.NET 6+ 支持"*",但老版本不行)
大目录下避免卡死的底线操作
超过 10 万文件的目录,GetFiles 会分配巨大数组,可能触发 GC 压力甚至 OutOfMemoryException(尤其 32 位进程)。这时候必须换思路:
- 别一次性加载所有路径,改用
EnumerateFiles+ 分批计数(比如每 1000 个打个日志) - 用
FileSystemEnumerableFactory(.NET 5+ 内部类,不公开)没意义,别折腾;公开 API 就是EnumerateFiles - 真正海量场景(千万级),考虑用 PowerShell 调
Get-ChildItem -File | Measure-Object或原生robocopy /L /S /NJH /FP /NS /NC输出行数——C# 不是干这个的
最常被忽略的一点:递归统计前,先用 Directory.GetDirectories 看一眼子目录深度和数量,比硬扫更早发现路径环、符号链接陷阱或权限断层。










