Should IObservable be preferred over events when exposing notifications in a library targeting .NET 4+

.net, c#, events, system.reactive

Solution

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.

Problem

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 `event`s 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.

Original source

Related problems