Observables are first-class types in .NET - meaning that you can keep a reference to them and pass them around as parameters to any constructor/method you like.
Events are not first-class types. They can only be attached and detached from in the scope that you can reference their containing object in.
So this means I cannot do this:
public void SomeMethod(EventHandler handler)
{
handler += (s, e) => { /* Handler Code */ };
}
public void SomeOtherMethod()
{
SomeMethod(Application.Current.Activated);
}
If I try that I get the error:
The event 'Application.Activated' can only appear on the left hand side of += or -=
That should let you know why you can't do var appActivated = Observable.FromEvent(Application.Current.Activated);
.
So, how can I work around this to attach events in SomeMethod
?
Here's how:
public void SomeMethod(Action<EventHandler> addHandler)
{
addHandler((s, e) => { /* Handler Code */ });
}
public void SomeOtherMethod()
{
SomeMethod(h => Application.Current.Activated += h);
}
Basically, in the method SomeMethod
the parameter is no longer EventHandler
, but Action<EventHandler>
. This means I am no longer trying to pass the event itself - instead I'm passing a way for the called code to attach itself to my event. The h
in the call to SomeMethod
is a promise that in the future if I were to have a valid handler then I can attach it by invoking the Action<EventHandler>
.
So let's say that I now want to write some code that knows how to attach and detach from an event. I now need this code:
public void SomeMethod(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
{
EventHandler handler = (s, e) => { /* Handler Code */ };
addHandler(handler);
/* Some Intervening Code */
removeHandler(handler);
}
public void SomeOtherMethod()
{
SomeMethod(h => Application.Current.Activated += h, h => Application.Current.Activated -= h);
}
In the /* Some Intervening Code */
code the handler is attached, and after it is detached.
This brings us to your code in your question:
var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);
This is very much the same as the SomeMethod
call above - FromEvent
needs a way for it to attach and detach from the event. The h
is a promise that says "hey, FromEvent
, if you can provide a handler, when you need it in the future, I promise that this code will attach it correctly." Or, detach, as the case may be.
Now, just to be a bit pedantic, your code should actually be:
IObservable<EventPattern<EventArgs>> appActivated =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);
Now that I have a IObservable<EventPattern<EventArgs>>
I can rewrite SomeMethod
to take this as a parameter and write it like this:
public IDisposable SomeMethod(IObservable<EventPattern<EventArgs>> appActivated)
{
return appActivated.Subscribe(ep => { /* Handler Code */ });
}
Now all of the power of Rx can be seen. The .Subscribe
method doesn't need any reference to the original event's containing object, but it will ultimately call h => Application.Current.Activated += h
to attach and h => Application.Current.Activated -= h
to detach as and when it needs. I can now effectively pass around events as first-class types in .NET.