这是另一种方法。与其传播快速连续事件中的第一个事件并压制所有随后的事件,现在除了最后一个事件外,所有事件都被压制。我认为可以从这种方法中受益的场景更为常见。
为了实现这一点,我们必须使用滑动延迟。每个传入事件都会取消将触发前一个事件的计时器,并重新启动计时器。这开启了永无止境的一系列事件将永远延迟传播的可能性。为简单起见,下面的扩展方法中没有针对这种异常情况的规定。
public static class FileSystemWatcherExtensions
{
public static IDisposable OnAnyEvent(this FileSystemWatcher source,
WatcherChangeTypes changeTypes, FileSystemEventHandler handler, int delay)
{
var cancellations = new Dictionary<string, CancellationTokenSource>(
StringComparer.OrdinalIgnoreCase);
var locker = new object();
if (changeTypes.HasFlag(WatcherChangeTypes.Created))
source.Created += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Deleted))
source.Deleted += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Changed))
source.Changed += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Renamed))
source.Renamed += FileSystemWatcher_Event;
return new Disposable(() =>
{
source.Created -= FileSystemWatcher_Event;
source.Deleted -= FileSystemWatcher_Event;
source.Changed -= FileSystemWatcher_Event;
source.Renamed -= FileSystemWatcher_Event;
});
async void FileSystemWatcher_Event(object sender, FileSystemEventArgs e)
{
var key = e.FullPath;
var cts = new CancellationTokenSource();
lock (locker)
{
if (cancellations.TryGetValue(key, out var existing))
{
existing.Cancel();
}
cancellations[key] = cts;
}
try
{
await Task.Delay(delay, cts.Token);
// Omitting ConfigureAwait(false) is intentional here.
// Continuing in the captured context is desirable.
}
catch (TaskCanceledException)
{
return;
}
lock (locker)
{
if (cancellations.TryGetValue(key, out var existing)
&& existing == cts)
{
cancellations.Remove(key);
}
}
cts.Dispose();
handler(sender, e);
}
}
public static IDisposable OnAllEvents(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.All, handler, delay);
public static IDisposable OnCreated(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Created, handler, delay);
public static IDisposable OnDeleted(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Deleted, handler, delay);
public static IDisposable OnChanged(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Changed, handler, delay);
public static IDisposable OnRenamed(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Renamed, handler, delay);
private struct Disposable : IDisposable
{
private readonly Action _action;
internal Disposable(Action action) => _action = action;
public void Dispose() => _action?.Invoke();
}
}
使用示例:
myWatcher.OnAnyEvent(WatcherChangeTypes.Created | WatcherChangeTypes.Changed,
MyFileSystemWatcher_Event, 100);
这一行结合了对两个事件的订阅,theCreated
和Changed
. 所以它大致相当于这些:
myWatcher.Created += MyFileSystemWatcher_Event;
myWatcher.Changed += MyFileSystemWatcher_Event;
不同之处在于这两个事件被视为单一类型的事件,并且在这些事件快速连续的情况下,只会传播最后一个事件。例如,如果一个Created
事件之后有两个Changed
事件,并且这三个事件之间没有大于 100 毫秒的时间间隔,则Changed
调用处理程序只会传播第二个事件MyFileSystemWatcher_Event
,而之前的事件将被丢弃。