Pregunta

.NET 4.0 beta 2 tiene introducido el IObservable y IObserver interfaces.

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

¿Fue útil?

Solución

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

Hay dos cosas importantes que debes entender sobre IObservable:

  1. Unifica dos conceptos que no sabíamos cómo unificar antes : operaciones asincrónicas (que generalmente devuelven un valor único) y eventos (que generalmente continúan para siempre).

  2. Es componible . A diferencia de los eventos CLR, IAsyncResult o INotifyCollectionChanged, nos permite construir 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 pueden 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 con 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 asíncrono. Debo esperar a 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, habría tenido que usar variables de estado para asegurar que los eventos se dispararan en cierto orden. También habría tenido que escribir un código bastante feo para separarme explícitamente del evento LayoutUpdated.

Cuando está programando con Rx, el truco es pensar "¿Qué evento deseo que proporcione mi marco?" y luego ve a crearlo. Estamos entrenados para pensar en los eventos como cosas simples, impulsadas por la entrada ("mouseover", "mouseclick", "keyup", etc.). Sin embargo, no hay ninguna razón por la cual los eventos no puedan ser muy complejos y específicos para su aplicación (" GoogleMsdnMashupStockDataArrived " ;, " DragStarting " ;, y " ImageContentChanged "). Cuando estructura sus programas de esta manera (cree exactamente el evento que necesito y luego responda cambiando el estado) encontrará que tienen menos errores de estado, se vuelven más ordenados y son más independientes -describiendo.

¿Entendido? :-)

Otros consejos

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 notificaciones

Los

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

Es solo una extensión del modelo de programación basado en eventos. Creas algo que implementa IObserver, y básicamente estás diciendo "esto es lo que quiero que suceda cuando algo cambia en la colección". 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 en comparación con el patrón IEnumerable. IEnumerable es '' pull '', mientras que IObservable es '' push ''.

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

Definitivamente deberías ver Rx Workshop: Observables versus eventos video y completar el desafío adjunto

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top