I want to create a Reactive
Observable
, which can observe Renamed
and Changed
at the same time.
I'm monitoring a folder for modified and renamed files with FileSystemWatcher
. I only want to handle the event after it has settled down (because sometimes Changed
event may be raised multiple times).
So I choose the Throttle
function of .net's Reactive Extension
.
I can manage to do the work by observing a single Changed
event, but can't work out how to observe Changed
and Renamed
in a single Observable
.
Here's what I've done for a single Changed
event:
var watcher = new FileSystemWatcher();
watcher.Path = "d:\\test";
var observable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => watcher.Changed += ev,
ev => watcher.Changed -= ev
);
var d = observable.Throttle(TimeSpan.FromSeconds(5))
.Subscribe(
a => Console.WriteLine($"Watcher type:{a.EventArgs.ChangeType }, file:{a.EventArgs.Name }, sender: {a.Sender}"),
ex => Console.WriteLine("observer error:" + ex.ToString()),
() => Console.WriteLine("observer complete.")
);
But when I want to watch for both the events, VS tells me that FileSystemEventHandler
and RenamedEventHandler
are not compatible (though RenamedEventArgs
can be safely cast to FileSystemEventArgs
):
var observable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => {
watcher.Changed += ev;
//Error
watcher.Renamed += ev;
},
ev => {
watcher.Changed -= ev;
//Error
watcher.Renamed -= ev;
});
I know I can convert the ev
to a compatible delegate , but I'm wondering how can I record that delegate and remove it from the event? Because I read that anonymous delegate can't be removed from event
unless record it at register time (as described in (this question)[Adding and Removing Anonymous Event Handler). Aslo, doing it this way same not elegant at all...
Func<FileSystemEventHandler, RenamedEventHandler> renameDelegate = (FileSystemEventHandler h) =>
{
return (object sender, RenamedEventArgs args) => h(sender, args);
};
var observable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => { watcher.Changed += ev;
//registered delegate
watcher.Renamed += renameDelegate(ev);
},
ev => { watcher.Changed -= ev;
//this seems not to be identical with the one registered
watcher.Renamed -= renameDelegate(ev); });
);
So how can I observer both Changed
and Renamed
event in the same Observable
?
Thanks to @Enigmativity 's answer, my final code goes like below(sample code):
var o1 = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => watcher.Changed += ev,
ev => watcher.Changed -= ev);
var o2 = Observable.FromEventPattern<RenamedEventHandler, RenamedEventArgs>(
ev => watcher.Renamed += ev,
ev => watcher.Renamed -= ev)
.Select(x => new System.Reactive.EventPattern<FileSystemEventArgs>(x.Sender, x.EventArgs));
var o = Observable.Merge(o1, o2);
var d = o.Throttle(TimeSpan.FromSeconds(5))
.Subscribe(
a => Console.WriteLine($"Watcher type:{a.EventArgs.ChangeType }, file:{a.EventArgs.Name }, sender: {a.Sender}"),
ex => Console.WriteLine("observer error:" + ex.ToString()),
() => Console.WriteLine("observer complete.")
Here's how you do it:
var observable =
Observable
.Using(
() => new FileSystemWatcher() { Path = "d:\\test" },
watcher =>
Observable
.Merge(
Observable
.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => watcher.Changed += ev,
ev => watcher.Changed -= ev)
.Select(x => new
{
x.EventArgs.Name,
x.EventArgs.FullPath,
x.EventArgs.ChangeType,
OldName = (string)null,
OldFullPath = (string)null,
}),
Observable
.FromEventPattern<RenamedEventHandler, RenamedEventArgs>(
ev => watcher.Renamed += ev,
ev => watcher.Renamed -= ev)
.Select(x => new
{
x.EventArgs.Name,
x.EventArgs.FullPath,
x.EventArgs.ChangeType,
x.EventArgs.OldName,
x.EventArgs.OldFullPath
})));