file.exists无系统缓存,每次调用均触发完整路径解析与元数据访问;建议短时高频场景用concurrentdictionary缓存,键需path.getfullpath规范化,过期设100–500ms。

Windows 文件系统本身不缓存 File.Exists 这类路径查询结果
很多人以为调用 File.Exists 多次,系统会自动记住上次结果,实际不是。NTFS 和 .NET 的 IO 层都不对单个路径存在性做用户态或内核态的“查询缓存”。每次调用都触发一次完整的路径解析 + 元数据访问(哪怕文件就在内存页中),尤其在 UNC 路径、挂载卷或杀毒软件 hook 重定向时延迟明显。
实操建议:
- 如果同一路径在短时间(比如一个请求周期)内被反复检查,自己加一层
ConcurrentDictionary<string bool></string>缓存,键用Path.GetFullPath(path)规范化,避免./a.txt和a.txt被当成两个键 - 缓存过期时间设为 100–500ms 即可——太长会错过外部进程创建/删除文件的变更;太短起不到作用
- 不要缓存
Directory.Exists的结果去推断子文件是否存在,目录存在 ≠ 子项可访问(权限、符号链接断裂、重解析点失效都会导致后续File.OpenRead失败)
Page.Cache 和文件系统 IO 没有直接关系
ASP.NET 的 Page.Cache(即 HttpContext.Current.Cache 或 MemoryCache)是纯内存对象缓存机制,它不感知、也不干预底层文件读写行为。你往里面存一个 byte[] 或 string,和磁盘上有没有对应文件、路径是否有效完全无关。
常见错误现象:
- 页面里写了
Page.Cache["config"] = File.ReadAllText("web.config"),但没处理FileNotFoundException,上线后配置文件名小写拼错,整个页面崩掉 - 缓存了
FileInfo对象,以为能免去重复new FileInfo(path)开销,其实FileInfo构造函数本身不访问磁盘,真正开销在.Length或.Exists上 - 把
Page.Cache当成文件变更监听器用——文件改了,缓存不会自动失效,必须手动Remove或用CacheDependency
真要实现文件变更自动刷新,得用 FileSystemWatcher 配合缓存失效
FileSystemWatcher 是唯一能低成本响应文件增删改的机制,但它不是“缓存”,只是通知器。它和缓存之间必须手动桥接,否则毫无意义。
使用场景与要点:
- 只监视具体文件(如
appsettings.json),别监视整个bin/目录——事件风暴会导致 CPU 爆高、漏事件、甚至 Watcher 崩溃 -
Changed事件可能触发多次(例如保存时编辑器先清空再写入),要用Threading.Timer去抖动,延迟 100ms 后再执行缓存更新 - 务必订阅
Error事件并记录日志:Watcher在远程 SMB 共享、USB 设备拔出等场景下会静默停止,不报错也不再触发事件 - 不要在
Changed回调里直接调用File.ReadAllText——IO 是阻塞的,会拖慢事件队列,应交由Task.Run或后台线程处理
性能差异主要来自路径解析和安全检查,不是“缓存命中率”
影响 File.Exists 或 File.Open 性能的关键,从来不是“有没有缓存”,而是三件事:路径字符串规范化开销、ACL 权限逐级检查、以及最终的磁盘/网络 IO 延迟。
参数差异与兼容性注意:
- 用
Path.GetRelativePath替代手拼路径字符串,避免..\..\导致解析失败或越权访问 - .NET 6+ 支持
File.Exists(string, System.IO.FileAttributes)重载,可跳过部分属性读取,但仅对 NTFS 有效,Linux/macOS 下退化为普通调用 - 在容器环境(如 Linux Docker)中,挂载的 Windows 主机路径经 CIFS/Samba 暴露时,
File.Exists可能返回false即使文件真实存在——这是协议层限制,加缓存也无解,得换用Directory.GetFiles扫描后匹配
最常被忽略的一点:缓存路径查询结果的前提,是你能接受“最多几百毫秒内看不到外部变更”。如果业务逻辑依赖强实时性(比如热插拔配置、多进程协同写日志),那任何用户态缓存都是危险的,不如老老实实每次查,再配好超时和重试。










