应优先使用 File.GetAttributes 单次获取属性判断路径类型,它可明确区分文件、目录、不可访问等状态,避免 Directory.Exists/File.Exists 的语义歧义,性能提升30%–40%,并需注意 ReparsePoint 等特殊属性及跨平台兼容性。

用 Directory.Exists 和 File.Exists 判断前先确认路径合法
直接调用这两个函数前,路径本身可能根本不存在、格式错误,或者权限不足——这时候它们都返回 false,但你无法区分是“真不是目录”还是“压根访问不了”。
常见错误现象:Directory.Exists(@"C:NotExistsSub") 返回 false,你以为它不是文件夹,其实连父目录都不存在。
实操建议:
- 先用
Path.IsPathRooted确认是不是绝对路径,相对路径容易在不同工作目录下行为不一致 - 再用
File.GetAttributes一次性获取属性,避免两次 IO(见下一条) - 若路径含通配符(如
*.txt)或 UNC 中的无效主机名,会抛ArgumentException,必须 try-catch
File.GetAttributes 是最稳的单次判断方式
它不关心“存在与否”的语义歧义,只读取 NTFS / FAT 的文件系统属性,返回值可明确区分:是文件、是目录、两者都不是、或根本不可访问。
使用场景:需要高可靠性判断(比如配置加载、资源扫描),或频繁调用需减少 IO 开销。
参数差异:传入路径字符串即可,不区分斜杠方向("C:/test" 和 "C:\test" 都行)。
性能影响:比分别调 Directory.Exists + File.Exists 快约 30%–40%,因为只查一次元数据。
示例:
var attr = File.GetAttributes(path);<br>if ((attr & FileAttributes.Directory) == FileAttributes.Directory)<br>{<br> // 是文件夹<br>}<br>else if (attr == FileAttributes.Normal || (attr & FileAttributes.Directory) == 0)<br>{<br> // 很可能是文件(注意:也可能是设备、重解析点等)<br>}注意:attr == FileAttributes.Normal 只适用于普通文件;如果路径是符号链接或压缩文件,属性位可能叠加,得用位与判断。
别忽略 FileAttributes.ReparsePoint 这个坑
Windows 下的软链接(symlink)、目录交接点(junction)、OneDrive 按需文件都会带 ReparsePoint 属性。此时仅靠 (attr & Directory) 可能误判——比如一个 junction 点指向真实目录,GetAttributes 返回的属性同时含 Directory 和 ReparsePoint,但它本身不是“原生目录”。
容易踩的坑:
- 用
DirectoryInfo构造时,如果路径是 junction,Exists返回true,但Parent可能为空或指向意外位置 - 递归遍历时,junction 可能造成无限循环(尤其没做路径去重)
-
File.GetAttributes对 OneDrive 按需文件返回Normal,但实际文件内容未下载,后续读取会触发同步或抛IOException
跨平台时优先用 FileSystemEnumerable 或第三方库
.NET 6+ 提供了 FileSystemEnumerable<T>,底层更适配 Linux/macOS 的 stat 行为,但 API 较底层;而 Directory.GetFileSystemEntries 在不同系统上对隐藏文件、大小写敏感性的处理不一致。
兼容性影响:
- Linux 上没有 “文件夹/目录” 的严格概念,一切皆文件,
Directory.Exists实际检查的是stat.st_mode & S_IFDIR - macOS 默认 HFS+ 大小写不敏感,但 APFS 可配成敏感,
Path.GetFileName在某些区域设置下可能截断失败 - Unity 或 .NET MAUI 等环境禁用部分 IO API,得改用
IFileSystem抽象层
NuGet 上的 Microsoft.Extensions.FileSystemGlobbing 更省心——它把路径分类逻辑封装好了,还自带缓存和模式匹配。









