Domanda

.NET 4.0 beta 2 ha introdotto il IObservable e IObserver .

Quali sono i vantaggi rispetto ai classici eventi .NET? Questo non risolve lo stesso problema?

È stato utile?

Soluzione

Puoi usare IObservable come evento, sostituendo il codice che espone eventi con proprietà di tipo IObservable, ma non è questo il punto.

Ci sono due cose importanti da capire su IObservable:

  1. Unifica due concetti che prima non sapevamo unificare : operazioni asincrone (che di solito restituiscono un singolo valore) ed eventi (che in genere continuano per sempre).

  2. È compostabile . A differenza degli eventi CLR, IAsyncResult o INotifyCollectionChanged ci consente di creare eventi specifici partendo da eventi generali e operazioni asincrone.

Ecco un esempio in cui mi sono imbattuto al lavoro proprio oggi pomeriggio.

In Silverlight ci sono alcuni effetti che puoi applicare a un controllo immagine che non può essere applicato a un controllo normale. Per aggirare queste limitazioni quando il contenuto di un controllo viene modificato, posso attendere che venga aggiornato il suo aspetto visivo e acquisirne uno screenshot. Quindi voglio nascondere la sua rappresentazione visiva, sostituirla con l'istantanea e applicare gli effetti visivi all'immagine. Ora posso applicare effetti di immagine a un controllo (supponendo che non sia interattivo).

Questo programma sarebbe banale ma per il fatto che deve essere asincrono. Devo attendere il completamento di due operazioni asincrone consecutive prima di poter applicare effetti all'immagine:

  1. Il contenuto del controllo è cambiato
  2. L'aspetto visivo del controllo viene aggiornato

Ecco come risolverei questo 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);
   }
}

Questo esempio è particolarmente semplice dato che ci sono solo due operazioni consecutive da sequenziare. Anche in questo semplice esempio, tuttavia, possiamo vedere che Rx aggiunge valore. Senza di essa avrei dovuto usare le variabili di stato per assicurarmi che gli eventi si attivassero in un certo ordine. Avrei anche dovuto scrivere un codice piuttosto brutto per staccare esplicitamente dall'evento LayoutUpdated.

Quando stai programmando con Rx il trucco è pensare " quale evento desidero che il mio framework fornisca? " e poi vai a crearlo. Siamo addestrati a pensare agli eventi come cose semplici, basate sull'input ("mouseover", "clic del mouse", "keyup", ecc.). Tuttavia non c'è motivo per cui gli eventi non possano essere molto complessi e specifici per la tua app (" GoogleMsdnMashupStockDataArrived " ;, " DragStarting " ;, e " ImageContentChanged "). Quando strutturi i tuoi programmi in questo modo (crea esattamente l'evento di cui ho bisogno e quindi rispondendo cambiando stato) scoprirai che hanno meno bug di stato, diventano più ordinati e sono del tutto più autonomi -describing.

Capito? :-)

Altri suggerimenti

Non sono sicuro dei vantaggi, ma vedo le seguenti differenze con gli eventi .NET classici:

notifiche di errore

Gli eventi classici richiederebbero un evento separato per questo, o una classe EventArgs con una proprietà Error che deve essere controllata.

notifica di fine notifica

Gli eventi classici richiederebbero un evento separato per questa o una classe EventArgs con una proprietà Final che deve essere controllata.

È solo un'estensione del modello di programmazione basato sugli eventi. Crei qualcosa che implementa IObserver e in pratica stai dicendo " ecco cosa voglio che accada quando qualcosa nella collezione cambia " ;. In questo modo, è solo una standardizzazione di ciò che tutti abbiamo fatto con gli eventi.

Lo stanno spingendo come se fosse un grande faccia a faccia rispetto al modello IEnumerable. IEnumerable è " pull " ;, mentre IObservable è " push " ;.

L'unico vantaggio che vedo sugli eventi diretti è che è un'interfaccia standardizzata. Vedo però una grande sovrapposizione con ObservableCollection qui (e INotifyCollectionChanged). Forse stanno cercando di adottare il motto PERL con .NET: " c'è più di un modo per farlo " ;.

Dovresti assolutamente guardare Rx Workshop: osservabili contro eventi video e completa la sfida allegata

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top