2

When creating an observable from an event, it looks like the following is the most common way:

var o = Observable.FromEventPattern(h => source.Event += h,
                                    h => source.Event -= h);

I find this form a little tedious in some cases where I would like to handle multiple events in the same way. But that doesn't look to easy, since the event it self seems impossible to parameterize, as shown in this non-compiling code:

private IObservable MakeAnObservableFromThisEvent(Event event)
{
    return Observable.FromEventPattern(h => event += h,
                                        h => event -= h);

}

private void MakeAlotOfObservables(object source)
{
    MakeAnObservableFromThisEvent(source.OneEvent);
    MakeAnObservableFromThisEvent(source.AnotherEvent);
    MakeAnObservableFromThisEvent(source.ThirdEvent);

    //or even
    MakeAnObservableFromThisEvent(() => source.ThirdEvent);
}

Of cause there is the 'event name'-overload:

var o = Observable.FromEventPattern< >(source, "Event");

but then there is this thing with more or less magic strings...

Is there away of optimizing this code? Or is this just the way things are?

Vegar
  • 12,828
  • 16
  • 85
  • 151

2 Answers2

2

The problem is that event handlers have "value-type" semantics (like strings) and so passing them as parameters is only useful if you intend to invoke them. Adding new handlers effectively creates a new delegate instance and the original is not modified.

Then only really viable method to add and remove handlers and maintain type safety at the same time is with the syntax you showed first in your question.

var o =
    Observable
        .FromEventPattern(
            h => source.Event += h,
            h => source.Event -= h);

However, there is another option that I've used quite a bit - and that's using extension methods.

If I have this class:

public class Foo
{
    public event EventHandler<EventArgs> Click;
}

I can write an extension method:

public static class FooEx
{
    public static IObservable<EventPattern<EventArgs>> Clicks(this Foo source)
    {
        return
            Observable
                .FromEventPattern<EventArgs>(
                    h => source.Click += h,
                    h => source.Click -= h);
    }
}

That then allows me to write this:

var foo = new Foo();
foo.Clicks().Subscribe(x => Console.WriteLine("Click!"));

You effectively write the extension method once per type and event and then can use it where ever you need to with a much improved syntax.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Events are certainly not value types (See the [Delegate Class](http://msdn.microsoft.com/en-us/library/system.delegate.aspx)); however, they are **immutable**. Furthermore, it's just the C# compiler that prevents you from reading or raising an event outside of the class that defines it - only += and -= are permitted. But you can always store the event in a variable or pass it as an argument from within the defining class. But since it's immutable, it won't help unless you use a `ref` parameter. It just seems pointless given that it's only useful from within the class that defines the event! – Dave Sexton Dec 09 '14 at 23:43
  • This will let me hide the tedious stuff away somewhere. Guess that's better than nothing :-) – Vegar Dec 10 '14 at 00:06
  • @Enigmativity Yep! But to be clear, a `ref` parameter could be used to answer the OPs question. I just think that it may be a bit pointless since its use would be limited to the class the defines the event. Perhaps it's worth defining anyway though. What do you think? Perhaps I'll add it to Rxx in the future. – Dave Sexton Dec 10 '14 at 01:03
  • @DaveSexton - Meaning that each class would have an private helper function that would look like this `private IObservable> MakeObservable(ref EventHandler @event)`? – Enigmativity Dec 10 '14 at 01:22
  • Yea, a helper function would be fine. But I'm just talking about a general-purpose extension method: `FromEventPattern(ref EventHandler @event)` – Dave Sexton Dec 10 '14 at 02:24
  • Note that it can't be implemented with lambdas. The closures must be reified similar to the operators found in the RxNet source code. And anyway, it would have to do what the other `FromEventPattern` overloads do; e.g., capture the `SynchronizationContext`, use a `Subject` as a proxy, etc. – Dave Sexton Dec 10 '14 at 02:26
  • 1
    @DaveSexton - I was going to say I don't think it'll work because you can't close over `ref` parameters. – Enigmativity Dec 10 '14 at 02:26
  • It's just complicated enough where it would make sense in a library like Rxx. – Dave Sexton Dec 10 '14 at 02:27
  • 1
    Oh I see, the `ref` probably wouldn't work regardless because even if you reify the closure C# doesn't let you capture the `ref` parameter by reference in a field. So it looks like C# is just too limited to get anything better than what currently exists in Rx. – Dave Sexton Dec 10 '14 at 02:30
2

It's not really a direct solution, but the ReactiveUI-Events basically implements what @Enigmativity suggests for the entire framework. So you can do something like:

Observable.Merge(
    Foo.Events().Clicked.Select(_ => Unit.Default),
    Foo.Events().KeyUp.Select(_ => Unit.Default));
Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • That's nice. It uses reflection and `dynamic`? I've been using a base class in my apps that does something similar (reflection + strings). I've also used a T4 template before, which I'll probably deploy over NuGet someday :-) – Dave Sexton Dec 10 '14 at 09:36
  • 2
    No reflection, it's pre-canned and done via code generation per-framework (so it only works for the built-in UI framework, not for 3rd party UI controls, but no `dynamic` == works on iOS) – Ana Betts Dec 10 '14 at 09:44