我有一个应用程序,我正在寻找一个文本文件,如果对文件做了任何更改,我使用OnChanged事件处理程序来处理事件。我正在使用NotifyFilters。LastWriteTime,但是事件仍然被触发两次。这是代码。

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

在我的情况下,OnChanged被调用两次,当我改变文本文件version.txt并保存它。


当前回答

您可以尝试打开它进行写入,如果成功,则可以假设其他应用程序已经处理了该文件。

private void OnChanged(object source, FileSystemEventArgs e)
{
    try
    {
        using (var fs = File.OpenWrite(e.FullPath))
        {
        }
        //do your stuff
    }
    catch (Exception)
    {
        //no write access, other app not done
    }
}

仅仅打开它以供写入似乎不会引发已更改的事件。所以应该是安全的。

其他回答

FileReadTime = DateTime.Now;

private void File_Changed(object sender, FileSystemEventArgs e)
{            
    var lastWriteTime = File.GetLastWriteTime(e.FullPath);
    if (lastWriteTime.Subtract(FileReadTime).Ticks > 0)
    {
        // code
        FileReadTime = DateTime.Now;
    }
}

我认为解决这个问题的最佳方案是使用响应式扩展 当你将事件转换为可观察对象时,你可以添加Throttling(..)(最初称为Debounce(..))

这里是示例代码

        var templatesWatcher = new FileSystemWatcher(settingsSnapshot.Value.TemplatesDirectory)
        {
            NotifyFilter = NotifyFilters.LastWrite,
            IncludeSubdirectories = true
        };

        templatesWatcher.EnableRaisingEvents = true;

        Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                addHandler => templatesWatcher.Changed += addHandler,
                removeHandler => templatesWatcher.Changed -= removeHandler)
            .Throttle(TimeSpan.FromSeconds(5))
            .Subscribe(args =>
            {
                _logger.LogInformation($"Template file {args.EventArgs.Name} has changed");
                //TODO do something
            });

FileSystemWatcher中的任何重复的OnChanged事件都可以通过检查File来检测和丢弃。GetLastWriteTime文件的时间戳。像这样:

DateTime lastRead = DateTime.MinValue;

void OnChanged(object source, FileSystemEventArgs a)
{
    DateTime lastWriteTime = File.GetLastWriteTime(uri);
    if (lastWriteTime != lastRead)
    {
        doStuff();
        lastRead = lastWriteTime;
    }
    // else discard the (duplicated) OnChanged event
}

很多答案都令人震惊,真的。 这里有一些代码从我的XanderUI控件库修复这个问题。

private void OnChanged(object sender, FilesystemEventArgs e)
{
    if (FSWatcher.IncludeSubdirectories == true)
    {
        if (File.Exists(e.FullPath)) { DO YOUR FILE CHANGE STUFF HERE... }
    }
    else DO YOUR DIRECTORY CHANGE STUFF HERE...
}

我在此将这段代码遗赠后人:

    static DateTimeOffset lastChanged = DateTimeOffset.UtcNow;
        static string lastChangedFile = null;

...

        private static void OnChanged(object sender, FileSystemEventArgs e)
        {
            if (e.ChangeType != WatcherChangeTypes.Changed || 
                (lastChanged.AddMilliseconds(500) > DateTimeOffset.UtcNow && lastChangedFile == e.FullPath)
               ) 
            {
                return;
            }
            lastChanged = DateTimeOffset.UtcNow;
            lastChangedFile = e.FullPath;
            Console.WriteLine($"Changed: {e.FullPath}");
            
        }