.NET 4.0 beta 2 has introduced the IObservable and IObserver interfaces.
What are the advantages compared to classic .NET events? Doesn\'t this solve the same problem?<
You should definitely watch Rx Workshop: Observables versus Events video and complete the attached Challenge
I'm not sure of the advantages, but I see the following differences with classic .NET events:
error notifications
Classic events would require a separate event for this, or an EventArgs
class with an Error
property that needs to be checked.
end-of-notifications notification
Classic events would require a separate event for this or an EventArgs
class with a Final
property that needs to be checked.
You can use IObservable as an event, replacing code that exposes events with properties of type IObservable, but that's not really the point.
There are two important things to understand about IObservable:
It unifies two concepts that we didn't know how to unify before: asynchronous operations (which typically return a single value) and events (which typically go on forever).
It is composable. Unlike CLR events, IAsyncResult, or INotifyCollectionChanged it allows us to build specific events out of general events and asynchronous operations.
Here's an example I ran into at work just this afternoon.
In Silverlight there are some effects you can apply to an image control that cannot be applied to a normal control. To get around these limitations when a control's content is changed I can wait for its visual appearance to be updated and take a screenshot of it. Then I want to hide its visual representation, replace it with the snapshot, and apply the visual effects to the image. Now I can apply image effects to a control (assuming it's not interactive).
This program would be trivial but for the fact that it must be asynchronous. I must wait for two consecutive asynchronous operations to complete before I can apply effects to the image:
Here's how I'd solve this problem using Rx:
// A content control is a control that displays content. That content can be
// anything at all like a string or another control. Every content control contains
// another control: a ContentPresenter. The ContentPresenter's job is to generate
// a visual representation of the Content property. For example, if the Content property
// of the ContentControl is a string, the ContentPresenter creates a TextBlock and inserts
// the string into it. On the other hand if the Content property is another control the
// ContentPresenter just inserts it into the visual tree directly.
public class MyContentControl : ContentControl
{
// A subject implements both IObservable and IObserver. When IObserver methods
// are called, it forwards those calls to all of its listeners.
// As a result it has roughly the same semantics as an event that we can "raise."
private Subject<object> contentChanged = new Subject<object>();
// This is a reference to the ContentPresenter in the ContentControl's template
private ContentPresenter contentPresenter;
// This is a reference to the Image control within ContentControl's template. It is displayed on top of the ContentPresenter and has a cool blur effect applied to it.
private Image contentImageControl;
public MyContentControl()
{
// Using Rx we can create specific events from general events.
// In this case I want to create a specific event ("contentImageChanged") which
// gives me exactly the data I need to respond and update the UI.
var contentImageChanged =
// get the content from the content changed event
from content in contentChanged
where content != null
// Wait for the ContentPresenter's visual representation to update.
// ContentPresenter is data bound to the Content property, so it will
// update momentarily.
from _ in contentPresenter.GetLayoutUpdated().Take(1)
select new WritableBitmap(contentPresenter, new TranslateTransform());
contentImageChanged.Subscribe(
contentImage =>
{
// Hide the content presenter now that we've taken a screen shot
contentPresenter.Visibility = Visibility.Collapsed;
// Set the image source of the image control to the snapshot
contentImageControl.ImageSource = contentImage;
});
}
// This method is invoked when the Content property is changed.
protected override OnContentChanged(object oldContent, object newContent)
{
// show the content presenter before taking screenshot
contentPresenter.Visibility = Visibility.Visible;
// raise the content changed "event"
contentChanged.OnNext(newContent);
base.OnContentChanged(oldContent, newContent);
}
}
This example is particularly simple given that there is only two consecutive operations to sequence. Even in this simple example though we can see that Rx adds value. Without it I would have had to have used state variables to ensure the events were firing in a certain order. I also would've had to write some pretty ugly code to explicity detach from the LayoutUpdated event.
When you're programming with Rx the trick is to think "What event do I wish my framework provided?" and then go create it. We're trained to think about events as simple, input-driven things ("mouseover", "mouseclick", "keyup", etc). However there's no reason events can't be very complex and specific to your app ("GoogleMsdnMashupStockDataArrived", "DragStarting", and "ImageContentChanged"). When you structure your programs this way (create exactly the event I need and then respond to it by changing state) you'll find that they have fewer state bugs, become more ordered, and are altogether more self-describing.
Got it? :-)
It's just an extension to the event based programming model. You create something that implements IObserver, and basically you're saying "here's what I want to happen when something in the collection changes". In that way, it's just a standardization of what we've all been doing with events.
They're pushing it like it's a big about-face compared with the IEnumerable pattern. IEnumerable is "pull", whereas IObservable is "push".
The only advantage I see over straight events is that it's a standardized interface. I see a big overlap with ObservableCollection here though (and INotifyCollectionChanged). Maybe they're trying to adopt the PERL motto with .NET: "there's more than one way to do it".