programming .net events system.reactive reactive-programming

.net - programming - system reactive nuget



Ventajas de.NET Rx sobre eventos clásicos (4)

.NET 4.0 beta 2 ha introduced las interfaces IObservable e IObserver .

¿Cuáles son las ventajas en comparación con los eventos clásicos de .NET? ¿No soluciona esto el mismo problema?



Es solo una extensión del modelo de programación basado en eventos. Usted crea algo que implementa IObserver, y básicamente está diciendo "esto es lo que quiero que suceda cuando algo en la colección cambie". De esa manera, es solo una estandarización de lo que todos hemos estado haciendo con los eventos.

Lo están presionando como si fuera un gran cambio de imagen en comparación con el patrón IEnumerable. IEnumerable es "pull", mientras que IObservable es "push".

La única ventaja que veo sobre los eventos simples es que es una interfaz estandarizada. Sin embargo, veo una gran superposición con ObservableCollection aquí (e INotifyCollectionChanged). Tal vez están tratando de adoptar el lema de PERL con .NET: "hay más de una forma de hacerlo".


No estoy seguro de las ventajas, pero veo las siguientes diferencias con los eventos clásicos de .NET:

notificaciones de error

Los eventos clásicos requerirían un evento separado para esto, o una clase EventArgs con una propiedad Error que debe verificarse.

notificación de fin de las notificaciones

Los eventos clásicos requerirían un evento separado para esta o una clase EventArgs con una propiedad Final que debe verificarse.


Puede usar IObservable como evento, reemplazando el código que expone eventos con propiedades de tipo IObservable, pero ese no es realmente el punto.

Hay dos cosas importantes que entender sobre IObservable:

  1. Unifica dos conceptos que antes no sabíamos cómo unificar : las operaciones asincrónicas (que generalmente devuelven un solo valor) y los eventos (que normalmente se repiten para siempre).

  2. Es composable . A diferencia de los eventos CLR, IAsyncResult o INotifyCollectionChanged nos permite crear eventos específicos a partir de eventos generales y operaciones asincrónicas.

Aquí hay un ejemplo que encontré en el trabajo esta tarde.

En Silverlight, hay algunos efectos que puede aplicar a un control de imagen que no se puede aplicar a un control normal. Para evitar estas limitaciones cuando se cambia el contenido de un control, puedo esperar a que se actualice su apariencia visual y tomar una captura de pantalla. Luego quiero ocultar su representación visual, reemplazarla por la instantánea y aplicar los efectos visuales a la imagen. Ahora puedo aplicar efectos de imagen a un control (suponiendo que no sea interactivo).

Este programa sería trivial, pero por el hecho de que debe ser asincrónico. Debo esperar que se completen dos operaciones asincrónicas consecutivas antes de poder aplicar efectos a la imagen:

  1. El contenido del control ha cambiado
  2. La apariencia visual del control se actualiza

Así es como resolvería este problema usando 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); } }

Este ejemplo es particularmente simple dado que solo hay dos operaciones consecutivas para secuenciar. Incluso en este simple ejemplo, podemos ver que Rx agrega valor. Sin él, tendría que haber usado variables de estado para asegurar que los eventos estuvieran encendidos en cierto orden. También habría tenido que escribir un código bastante feo para separarlo explícitamente del evento LayoutUpdated.

Cuando estás programando con Rx, el truco es pensar "¿Qué evento deseo que proporcione mi framework?" y luego ve a crearlo. Estamos entrenados para pensar sobre eventos como cosas simples, basadas en la entrada ("mouseover", "mouseclick", "keyup", etc.). Sin embargo, no hay motivo por el que los eventos no puedan ser muy complejos y específicos para su aplicación ("GoogleMsdnMashupStockDataArrived", "DragStarting" e "ImageContentChanged"). Cuando estructuras tus programas de esta manera (creas exactamente el evento que necesito y luego respondes cambiándolo de estado), encontrarás que tienen menos errores de estado, se ordenan más y son más autodescriptivos.

¿Lo tengo? :-)