かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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!!