2

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);
Bad
  • 4,967
  • 4
  • 34
  • 50
  • 1
    I wrote a long explanation and discussion of FromEvent a while back that might be of use: http://stackoverflow.com/questions/19895373/how-to-use-observable-fromevent-instead-of-fromeventpattern-and-avoid-string-lit/19896246#19896246 – James World Mar 09 '16 at 16:31
  • 2
    @James World: I had 60-70% of understanding after reading answers here. It's >95% after reading your post. Especially your paragraph beginning with "Note that it's only that act of calling Subscribe that creates the subscription." helped me a lot. – Bad Mar 09 '16 at 17:58
  • Thanks for the feedback. It's a surprisingly deep method, and needs a lot of explaining! – James World Mar 11 '16 at 00:01

3 Answers3

6

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.

Community
  • 1
  • 1
Eren Ersönmez
  • 38,383
  • 7
  • 71
  • 92
3

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    And for your last point, he's [presumably] not writing this code from the class defining that event, so it's invalid to even access the handler list, there is no way to write a `FromEvent` method for which that code could compile, let alone do what it needs to do. – Servy Mar 08 '16 at 19:06
  • What I actually still do not understand is the following: we have events and we want to make from them a stream of events. So there is no mention of any handler here. And this `h` handler as if appears and disappears from nowhere. Sorry, maybe I do not understand something conceptually. – Bad Mar 08 '16 at 20:01
  • 1
    @Bad: Do you perhaps not understand what a lambda is? When you say `customers.Select(c => c.FirstName)` the `c` just appears "out of nowhere". Do you understand that a lambda is just a short way of writing a method? – Eric Lippert Mar 08 '16 at 20:46
  • 1
    @Bad: Suppose instead we wrote `void Adder(EventHandler h) { Activated += h; }` and then passed `new Action(Adder)` to the factory. Would you then say "where did the h come from?" or is it now clear? A lambda is just a short way of writing that method. – Eric Lippert Mar 08 '16 at 20:51
  • @Eric: Yes, of course, I do. In lambda that you wrote, `c` is just an input parameter / argument. Let me clarify my question: we have simple async events, from which we want to make `Observable`, aka stream of events, aka sequence of events. We do not yet `handle` any of these events. As far as I understand a `handler` will appear when we subscribe to this newly created `Observable` and write code in which we will make something with each event from `Observable`, i.e. we will `handle` each event from a sequence. But at the point when we make `Observable` we do not handle? Or do we? – Bad Mar 08 '16 at 20:53
  • In other words I thought that the process was as follows: simple events --> (by means of `FromEvent` are transformed to) `Observable` of events --> (only now we) `Handle` each event from `Observable`. – Bad Mar 08 '16 at 20:56
  • 1
    @Bad: The observable is going to surface a sequence of values produced by the event firing. In order for it to do so, *it needs to subscribe to the event*. How does it do so? The observable does not know how to subscribe to events. You need to tell it how to do so. So you give it a delegate that knows how to do so. Is that clear? – Eric Lippert Mar 08 '16 at 21:09
  • @Bad what you do with the observable afterwards is your business. The point of the lambdas is so that the observable knows how to get notifications from the event, not so that you get notifications from the observable. – Eric Lippert Mar 08 '16 at 21:12
  • @Eric: Thank you for clarification. – Bad Mar 08 '16 at 21:16
  • @EricLippert - I've had a go at answering this question from a different direction. I would very much appreciate it if you would take a look and let me know if you feel like I've explained it properly. – Enigmativity Mar 09 '16 at 05:14
  • @Enigmativity: Looks reasonable. – Eric Lippert Mar 09 '16 at 14:47
2

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172