30

I have a .NET library which, as part of an Object Model will emit notifications of certain occurrences.

It would seem to me that the main pros of events are approachability for beginners (and simplicity in certain consumption contexts) with the main negative being that they are not composable and hence are immediately forced into an Observable.FromEvent* if you want to do anything interesting without writing a code thicket.

The nature of the problem being solved is such that the event traffic won't be particularly frequent or voluminous (it's definitely not screaming RX), but there is definitely no requirement to support .NET versions prior to 4.0 [and hence I can use the built-in IObservable interface in System.Reactive without forcing any significant dependencies on consumers]. I'm interested in some general guidelines some specific concrete reasons to prefer IObservables over events from an API design perspective though - regardless of where my specific case might sit on the event - IObservable spectrum.

So, the question:

Is there anything concrete I'm making dramatically more difficult or problematic for API consumers if I go with the simplest thing and expose an event instead of an IObservable

Or, restated: Aside from the consumer having to do an Observable.FromEvent* to be able to compose events, is there really not a single reason to prefer an IObservable over an event when exposing a notification in an API?

Citations of projects that are using IObservable for not-screaming-RX stuff or coding guidelines would be ideal but are not critical.


NB as touched on in the comments with @Adam Houldsworth, I'm interested in concrete things wrt the API surface of a .NET 4+ library, not a survey of opinions as to which represents a better 'default architecture' for our times :)

NB this question has been touched on in IObserver and IObservable in C# for Observer vs Delegates, Events and IObservable vs Plain Events or Why Should I use IObservable?. The aspect of the question I'm asking has not been addressed in any of the responses due to SRP violations. Another slightly overlapping question is Advantages of .NET Rx over classic events?. Use of IObservable instead of events falls into that same category.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • @AdamHouldsworth For me this question has little to do with RX - `System.Reactive` and `IObservable` is a standard part of .NET and I'm not considering for one minute taking or forcing on the consumers a dependency on RX. (This is slightly coloured for me by the fact that the F# standard library has some nice library support for consuming `IObservable`s) – Ruben Bartelink Jul 04 '14 at 10:52
  • @AdamHouldsworth Yes but the RX team went to great lengths to get `System.Reactive` [and `IObservable`] into the core for a reason. As I cited, F# has good consumption support and there's no reason why other languages couldnt provide similar. And while you're right that RX is the elephant in the room, there's more than one way to compose events (thinking F# agents...) BTW I'm not arguing with your central thesis that this is opinion territory but I def dont want a programmers.SE answer – Ruben Bartelink Jul 04 '14 at 11:00
  • [This question](http://stackoverflow.com/questions/11189944/use-of-iobservable-instead-of-events) might be worth reviewing. – Adam Houldsworth Jul 04 '14 at 14:16
  • No problem. I was looking at it from an objective view and realised the comments themselves might not add much, though the amendments to the question are useful. I am still reading around this topic as I find it quite interesting. – Adam Houldsworth Jul 04 '14 at 14:23
  • @AdamHouldsworth While that's an interesting question, for me it's 'just' an architecture question. Deleted first comment; will delete this after you ack – Ruben Bartelink Jul 04 '14 at 14:39

4 Answers4

15

In the comments of this answer, OP refined his question as:

[Is it] indeed definitely the case that each and every event can always be Adapted to be an IObservable?

To that question, the answer is basically yes - but with some caveats. Also note that the reverse is not true - see the section on reverse transformation and the conclusion for reasons why observables might be preferred to classic events because of the additional meaning they can convey.

For strict translation, all we need to do is map the event - which should include the sender as well as arguments - on to an OnNext invocation. The Observable.FromEventPattern helper method does a good job of this, with the overloads returning IObservable<EventPattern<T>> providing both the sender object and the EventArgs.

Caveats

Recall the Rx grammar. This can be stated in EBNF form as: Observable Stream = { OnNext }, [ OnError | OnCompleted ] - or 0 or more OnNext events optionally followed by either an OnCompleted or an OnError.

Implicit in this is the idea that from the view of an individual subscriber events do not overlap. To be clear, this means that a subscriber will not be called concurrently. Additionally, it is quite possible that other subscribers can be called not only concurrently but also at different times. Often it is subscribers themselves that control pace of event flow (create back-pressure) by handling events slower than the pace at which they arrive. In this situation typical Rx operators queue against individual subscribers rather than holding up the whole subscriber pool. In contrast, classic .NET event sources will more typically broadcast to subscribers in lock-step, waiting for an event to be fully processed by all subscribers before proceeding. This is the long-standing assumed behaviour for classic events, but it is not actually anywhere decreed.

The C# 5.0 Language Specification (this is a word document, see section 1.6.7.4) and the .NET Framework Design Guidelines : Event Design have surprisingly little to say on the event behaviour. The spec observes that:

The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events.

The C# Programming Guide : Events section says that:

When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised. To invoke events asynchronously, see Calling Synchronous Methods Asynchronously.

So classic events are traditionally issued serially by invoking a delegate chain on a single thread, but there is no such restriction in the guidelines - and occasionally we see parallel invocation of delegates - but even here two instances of an event will usually be raised serially even if each one is broadcast in parallel.

There is nothing anywhere I can find in the official specifications that explicitly states that event instances themselves must be raised or received serially. To put it another way, there is nothing that says multiple instances of an event can't be raised concurrently.

This is in contrast to observables where it is explicitly stated in the Rx Design Guidelines that we should:

4.2. Assume observer instances are called in a serialized fashion

Note how this statement only addresses the viewpoint of an individual subscriber instance and says nothing about events being sent concurrently across instances (which in fact is very common in Rx).

So two takeways:

  • Whilst OnNext captures the idea of an event it is possible that the classic .NET event may violate the Rx Grammar by invoking events concurrently.
  • It is common for pure Rx Observables to have different semantics around the delivery of events under load because back-pressure is typically handled per subscriber rather than per-event.

As long as you don't raise events concurrently in your API, and you don't care about the back-pressure semantics, then translation to an observable interface via a mechanism like Rx's Observable.FromEvent is going to be just fine.

Reverse Transformation

On the reverse transformation, note that OnError and OnCompleted have no analogue in classic .NET events, so it is not possible to make the reverse mapping without some additional machinery and agreed usage.

For example, one could translate OnError and OnCompleted to additional events - but this is definitely stepping outside of classic event territory. Also, some very awkward synchronization mechanism would be required across the different handlers; in Rx, it is quite possible for one subscriber to receive an OnCompleted whilst another is still receiving OnNext events - it's much harder to pull this off in a classic .NET events transformation.

Errors

Considering behaviour in error cases: It's important to distinguish an error in the event source from one in the handlers/subscribers. OnError is there to deal with the former, in the latter case both classic events and Rx simply (and correctly) blow up. This aspect of errors does translate well in either direction.

Conclusion

.NET classic events and Observables are not isomorphic. You can translate from events to observables reasonably easily as long as you stick to normal usage patterns. It might be the case that your API requires the additional semantics of observables not so easily modelled with .NET events and therefore it makes more sense to go Observable only - but this is a specific consideration rather than a general one, and more of a design issue than a technical one.

As general guidance, I suggest a preference for classic events if possible as these are broadly understood and well supported and can be transformed - but don't hesitate to use observables if you need the extra semantics of source error or completion represented in the elegant form of OnError and OnCompleted events.

James World
  • 29,019
  • 9
  • 86
  • 120
3

I've been reading a lot about Reactive extensions before finally dipping my toe, and after some rough starts I found them really interesting and useful.

Observables extension have this lovely optional parameter where you can pass your own time manager, effectively letting you manipulate time. In my case it helped a lot since I was doing some time related work (check this webservice every ten minutes, send one email per minute, etc) and it made testing a breeze. I would plug the TestScheduler in the component under test and simulate one day of events in a snap.

So if you have some workflows in your library where time plays a role in orchestration, I would really recommend using Observables as your output.

However if you are just raising events in direct responses to user inputs, I don't think that it's worth the added complexity for your users. As you noted they can wrap the event into their own Observable if needed.

You could have your cake and eat it too, although it would mean more work; offer a facade that turns your event library into a Observable-fueled one, by creating said Observable from your events. Or do it the other way: have an optional facade that suscribes to your observable and raise a classic event when triggered.

In my opinion there is a non-trivial technical step to take when dealing with reactive extensions and in this case it may come down to what your API consumers would be the most comfortable using

samy
  • 14,832
  • 2
  • 54
  • 82
  • Sorry, as with the other answers, I obviously need to edit the question more - I'm seeking to confirm **whether** people will be able to Adapt from an `event` or whether there are some cases where it is actually **necessary** for it to be an `IObservable` in the first place ? – Ruben Bartelink Jul 04 '14 at 14:56
  • Have heavily edited the question now - apologies for the lack of clarity in the original (Sadly despite the obvious quality of your answer I can't really upvote it as it doesnt answer my question. I'd be very happy to upvote it were it re-posted as an answer to one of those) – Ruben Bartelink Jul 04 '14 at 15:14
1

The IObservable is the IEnumerable of events, so the only question here is do you think IObservable will become the standard as the IEnumerable is now so yes its preferable, if you think its just a passing by thing that will fall in future use event instead.

The IObservable is better than event in most cases but I personally think I'll forget to use it as its not very common to be used and when the time arrives I'll have forgotten about it.

I know my answer is not of great help but only time will tell if RX will become the standard, I think it has good chances.

[EDIT] To make it more Concrete.

To the end user the only difference is that one is an interface an the other is not, making the interface more testable and expandable, because different sources can implement the same interface.

As Adam Houldsworth said one can be changed to the other easily to make no other difference.

Pedro.The.Kid
  • 1,968
  • 1
  • 14
  • 18
  • `IObservable` is in .NET Core and has made it to plenty platform bindings. But so have `event`s. As mentioned in my comment discussion with @AdamHouldsworth, this really is not about RX. And my question is about what I'm going do do in my API right now -- for a **concrete** reason, not on any future proofing or Architecture Astronaut basis. – Ruben Bartelink Jul 04 '14 at 13:09
  • 2
    @RubenBartelink A concrete reason can only be realised based on what you know of the requirements of those consuming it. I think the important thing is to retain consistency. At the end of the day, if you were to raise events and someone wanted an observable, they'd plumb around it and move on. Vice versa for being given an observable and wanting an event. I personally think the public contract of either is the same / similar enough to not matter, it then comes down to consumption style. And that discussion was without value because I wasn't mentioning Rx as critical to your question. – Adam Houldsworth Jul 04 '14 at 13:55
  • @Pedro.The.Kid Re the edit. My question is all about asking if there is any difference. i.e. your "one can be changed to the other easily to make no other difference" contention is the very thing I'm questioning. And I'm talking about a property I'm exposing in an object model, so minus some minor messing there's no massive difference between how/if they'd consume it in a test [though for *me* to test it `IObservable` is a tiny bit neater in F# vs doing it with events - but again nothing that can't be wrapped neatly in 2 lines of code like everything in F#] – Ruben Bartelink Jul 04 '14 at 14:43
  • 1
    @AdamHouldsworth As mentioned to Pedro I'm only asking if there are concrete reasons. Your consistency point is definitely critical though. As it happens I'm leaning towards `event`s on a KISS basis. I'm looking for something to convince me that there is a single benefit to exposing `IObservable` instead aside from people having to Adapt `event`s for a real reason. – Ruben Bartelink Jul 04 '14 at 14:49
  • How about a more explicit disposal and subscription pattern? `IObservable` is more explicitly a design approach that encourages a certain style of coding and awareness of subscribers and object life-cycles. This might have a more positive effect on the potential for memory leaks, often realised from poor awareness and management of event subscriptions. I personally bend towards Reactive Programming where I can (which is hard on our legacy app, but hey ho!) and with it come a set of development patterns and practices that offer their own advantages. However, without a known need, KISS wins. – Adam Houldsworth Jul 04 '14 at 14:52
  • Question heavily edited. Would appreciate a revision/delete of answer as it's all getting a bit messy at this stage? – Ruben Bartelink Jul 04 '14 at 15:08
  • @AdamHouldsworth If the answer to my revised question is "Yes, you can always adapt an event" then they can wrap it (and obviously the rest of your point is defintely right on aside from that) – Ruben Bartelink Jul 04 '14 at 15:10
1

To address your headline question:

No they should not be preferred on the basis that they exist in .NET 4 and are available to use. Preference depends on intended use, so a blanket preference is unwarranted.

That said, I would tend towards them as an alternative model to traditional C# events.

As I have commented on throughout this question, there are many ancillary benefits to approaching the API with IObservable, not least of which is external support and the range of choice available to the end consumer.

To address your inner question:

I believe there would be little difficulty between exposing events or IObserable in your API as there is a route to one from the other in both cases. This would put a layer over your API, but in actuality this is a layer you could also release.

It is my opinion that choosing one over the other isn't going to be part of the deciding reason why someone choose to use or not use your API.

To address your re-stated question:

The reason might be found in why there is an Observable.FromEvent in the first place :-) IObservable is gaining support in many places in .NET for reactive programming and forms part of many popular libraries (Rx, Ix, ReactiveUI), and also interoperates well with LINQ and IEnumerable and further into the likes of TPL and TPL DataFlow.

A non-Rx example of the observable pattern, so not specifically IObservable would be ObservableCollection for XAML apps.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • Thanks again for the input and effort but for me it's all about if there is any benefit that one cannot possibly derive if you use an `event` without a pain greater than the consumer using `Observable.FromEvent*` if they see fit instead of using an `IObservable` in the first place. – Ruben Bartelink Jul 04 '14 at 15:28