Vantaggi di .NET Rx rispetto agli eventi classici?
-
06-07-2019 - |
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?
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:
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).
È 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:
- Il contenuto del controllo è cambiato
- 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