FileSystemWatcher 是监控单个文件或目录的最优选择,但需设 IncludeSubdirectories=true 才递归监听;EnableRaisingEvents 必须最后设置;事件回调需线程安全且避免耗时操作;NotifyFilter 与 Filter 需协同配置;缓冲区溢出会导致事件丢失,可调大 InternalBufferSize 或改用轮询。

用 FileSystemWatcher 监控单个文件或目录最直接
它专为这个场景设计,底层调用 Windows API(ReadDirectoryChangesW),资源开销低、响应快。但默认只监控路径下的**一级子项**,不会递归监听子目录——这点常被忽略。
常见错误是直接监听 C:\temp 却期望捕获 C:\temp\sub\file.txt 的修改,结果没触发事件。必须显式设置 IncludeSubdirectories = true 才行。
实操建议:
-
EnableRaisingEvents = true必须在配置完所有属性后才设为true,否则可能漏掉初始事件 - 对同一路径多次实例化
FileSystemWatcher会导致重复触发,建议单例或严格管理生命周期 - 事件回调(如
Changed)运行在非 UI 线程,更新 WinForm/WPF 控件前需Invoke或Dispatcher.BeginInvoke - 不要在事件处理器里做耗时操作(如读大文件、网络请求),否则会阻塞后续文件事件队列
监控特定文件类型或跳过临时文件要靠 Filter 和 NotifyFilter
Filter 控制匹配的文件名模式,比如 "*.log" 或 "config.json";而 NotifyFilter 决定监听哪些变更类型——它不是布尔开关组合,而是位运算值。
典型误用:设了 Filter = "*.txt" 却没改 NotifyFilter,结果连文件重命名都收不到。因为默认 NotifyFilter 只含 FileName | DirectoryName,不包含 LastWrite。
常用组合:
- 只关心内容修改:
NotifyFilter = NotifyFilters.LastWrite - 同时关注新建/删除/重命名:
NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.CreationTime - 跳过编辑器生成的临时文件(如 VS 的
.tmp或~$xxx.docx):在事件处理器里加白名单判断,if (e.Name.StartsWith("~$") || e.Name.EndsWith(".tmp")) return;
遇到“事件丢失”或“重复触发”,大概率是缓冲区溢出或文件操作原子性问题
FileSystemWatcher 内部有个固定大小的缓冲区(默认 8KB),当短时间内大量文件变动(如解压百个文件、Git checkout),缓冲区填满就会丢弃后续事件,抛出 Error 事件,错误信息是 "The directory name is invalid." 或更隐蔽的静默丢失。
这不是 Bug,是设计使然。解决方式只有两个:
- 增大
InternalBufferSize(最大 64KB),但治标不治本,且内存占用上升 - 改用轮询(
Timer+Directory.GetFiles()+ 时间戳比对)——适合小目录、低频变更、或必须 100% 不丢事件的场景
另一个高频现象:用记事本保存文件时触发两次 Changed。这是因为记事本先写新内容到临时文件,再原子替换原文件,系统上报了两次 LastWrite。应对策略是在事件处理器里加防抖逻辑,例如缓存上次处理时间,if (DateTime.Now.Subtract(lastProcessed).TotalMilliseconds
跨平台或需要更高可控性时,别硬扛 FileSystemWatcher
它在 Linux/macOS 上依赖 .NET 的 inotify / kqueue 封装,行为不一致:比如 macOS 对符号链接支持弱,Linux 某些容器环境禁用 inotify 会导致完全失效。
如果项目已引入 Microsoft.Extensions.FileSystemGlobbing 或需要和 ASP.NET Core 配置热重载机制对齐,可考虑用 IFileProvider.Watch(),它抽象了底层差异,返回 IChangeToken,语义更清晰。
但注意:IFileProvider.Watch() 默认也是基于 FileSystemWatcher 实现的,只是加了一层包装——真正脱离它的方案,目前只有手动轮询或借助第三方库如 LiteGuard(轻量)或 Humanizer(不推荐,仅作示例)。
复杂点始终在边界:文件锁、权限变化、网络驱动器断连、长路径(>260 字符)、Unicode 文件名编码……这些情况 FileSystemWatcher 都不会主动报错,只会静默失败。上线前务必在目标环境中实测真实文件流。










