Reputation: 5309
What is the purpose of having such a signature in Observable.FromEvent
?
For example:
var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);
In particular, what is h
? And why +=
, then -=
? Do we make Observable
from event or from event handler? If from event, why not just have a signature like:
var appActivated = Observable.FromEvent(Application.Current.Activated);
Upvotes: 2
Views: 1127
Reputation: 39085
That's because there's no way to pass in an event as a parameter to a method. You could pass in the event as a delegate but that doesn't give you the ability to subscribe/unsubscribe to the event. See this answer by Eric Lippert.
Observable.From
basically says "Ok, I will give you an observable that is a wrapper around the event, but you need to provide me with two delegates: 1) a delegate for me to subscribe my handler to the event, and 2) a delegate for me to unsubscribe my handler when I need to".
So in this case h => Application.Current.Activated += h
is a lambda expression that gets compiled into a delegate. h
(handler) is the input parameter, and the delegate takes that input parameter and subcribes it to the Activated
event. And the second delegate is the same thing, except it unsubscribes the handler.
Upvotes: 6
Reputation: 117104
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.
Upvotes: 2
Reputation: 660249
Eren's answer is correct; I want to make sure that all your questions are answered:
In particular, what is h?
h is a parameter to the delegates which add and remove handlers. When invoked, h will be a reference to a handler delegate.
And why +=, then -=?
The observable requires the ability to both subscribe and unsubscribe handlers to the event.
Do we make Observable from event or from event handler?
From an event.
If from event, why not just have a signature like:
var appActivated = Observable.FromEvent(Application.Current.Activated);
?
Because that would pass the handler, not the event. An "event" is three things: the ability to invoke a handler list, the ability to add a new handler to the list, and the ability to remove a handler from the list. The observable needs the last two; your proposal is to pass the first. So the observable takes delegates which do the last two.
Upvotes: 3