かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

FileSystemWatcherの連続して発生する同じ種類のイベントを無視る

id:trapemiyaさんのBlog記事のFileSystemWatcherですが、こいつを扱うのは厄介らしいです。

ということでid:trapemiyaさんの対処方法はChangedイベント発生後10秒間は次のイベントを無視るというものでした。ちょっと対処方法は変わりますが、Rxを使うと10秒間イベントが発生しなかったら・・・といった方法での対応がさくっとできます。

using System;
using System.IO;
using System.Reactive.Linq;

namespace FileWatchTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 監視対象のディレクトリ
            var dir = @"C:\Users\Kazuki\Documents\IISExpress";
            
            // とりあえず適当に監視する
            var watcher = new FileSystemWatcher(dir);
            watcher.Filter = "";
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.LastAccess | NotifyFilters.FileName | NotifyFilters.Security;
            
            // Rx本領発揮
            watcher.ChangedAsObservable()
                // 10秒待つ
                .Throttle(TimeSpan.FromSeconds(10))
                .Subscribe(e => Console.WriteLine("{0}: {1}.", e.FullPath, e.ChangeType));
            
            // 監視開始
            watcher.EnableRaisingEvents = true;
            // 待ち
            Console.ReadLine();
        }
    }

    static class FileSystemWatcherExtensions
    {
        // ChangedイベントをIObservable<TEventArgs>にするヘルパーメソッド
        public static IObservable<FileSystemEventArgs> ChangedAsObservable(this FileSystemWatcher self)
        {
            return Observable.FromEvent<FileSystemEventHandler, FileSystemEventArgs>(
                h => (_, e) => h(e),
                h => self.Changed += h,
                h => self.Changed -= h);
        }
    }
}

厳密に同じにしたかったら、IObservableのシーケンスに対して一度値を発行したら10秒間は値が発行されても無視るみたいな拡張メソッド定義してもよさげですね。
ビバRx!!