L'estensione .Net Reactive offre agli sviluppatori un insieme di funzionalità per implementare un modello di programmazione reattiva per gli sviluppatori .Net, al fine di rendere la gestione degli eventi più semplice ed espressiva tramite azioni dichiarative. Sebbene i pilastri fondamentali dello scaling reattivo siano le interfacce IObserver e IObservable, come sviluppatore spesso non è necessario implementare queste interfacce da soli. La libreria supporta il tipo integrato Subject<T>, che implementa interfacce e supporta molte funzioni.
I temi sono la base per i diversi argomenti disponibili nella libreria, e ci sono altri temi - <T>ReplaySubject,<T> BehaviorSubject e <T>AsyncSubject. È utile comprendere le differenze essenziali tra loro e come usarle per sfruttare meglio la libreria.
In questo articolo confronteremo il Soggetto<T> e il suo fratello, cercando di illustrare le differenze tra i loro comportamenti.
Oggetto<T>
Come detto prima, Subject<T> è la base per i temi disponibili, offrendo un modo semplice per usare la libreria senza dover<T> implementare direttamente le interfacce IObservable e<T> IObserver. Di seguito è mostrata una semplice dimostrazione del tipo di tema.
Nel codice sopra, abbiamo creato <T>un'istanza di Subject e, poiché implementa<T> IObserver e IObserverable<T>, usiamo la stessa istanza per iscriversi e pubblicare il valore su IObserver. Un altro punto importante da notare qui è come usiamo il sovraccarico del metodo Subscribe per accettare azioni come input. Questo verrà fatto per ogni valore pubblicato, in questo caso stampando il numero sulla console.
Cerchiamo di mostrare i valori pubblicati e quelli che IObserver (in questa Azione<T>) stampa alla console nell'immagine seguente. Questo ci aiuterà a confrontare facilmente i fratelli e le varianti rimanenti.
La prima riga rappresenta il valore pubblicato, mentre la seconda riga rappresenta il valore ricevuto dall'IObserver. Inoltre, abbiamo aggiunto una riga che indica in quale momento l'osservatore si abbona al flusso durante l'esecuzione. Questa linea è rappresentata da una linea tratteggiata verticale.
Nel codice sopra, abbiamo notato che l'osservatore si è sottoscritto al flusso dati prima di pubblicare il primo valore. L'immagine mostra la linea degli Abbonati posta prima del primo elemento. Come si vede dalla linea di uscita, questo non ha alcun effetto sull'uscita (a questo punto).
Ma cosa succede se l'osservatore si abbona ai dati solo dopo che alcuni valori sono già stati pubblicati? Questo ha un impatto sui dati ricevuti dagli osservatori? Prima di guardare l'output, scriviamo prima lo stesso codice.
Nel codice sopra, possiamo osservare che l'osservatore si abbona al flusso dati solo dopo che due valori (1 e 2) sono stati pubblicati. Come ci si potrebbe aspettare, questo farà sì che gli osservatori non ricevano i dati pubblicati prima di chiamare il metodo di abbonamento. Come mostrato nella figura sottostante.
E se volessi leggere tutti i valori pubblicati, anche se l'osservatore si iscrive in ritardo? Ed è qui che entra in gioco<T> ReplaySubject.
ReplaySubject<T>
ReplaySubject<T> memorizza i valori nella cache e li riproduce per gli abbonati successivi. Questo è utile per evitare le condizioni di gara. Cambiamo il codice precedente per usare<T> ReplaySubject e vediamo come influisce su ciò che l'osservatore riceve.
Come mostrato nel codice sopra,<T> <T>c'è pochissima variazione nel codice, tranne che ora usiamo ReplaySubject invece di subject. Il diagramma seguente illustra l'impatto sui dati ricevuti dall'osservatore.
Come mostrato nell'immagine, il valore memorizzato nella cache viene ora riprodotto all'abbonato anche se l'abbonato si abbona successivamente. Naturalmente, questa funzione utile ha un prezzo. Questa implementazione memorizzerà ogni valore pubblicato dall'abbonato, il che può causare problemi di memoria quando la quantità di dati è significativamente maggiore.
Tuttavia, ReplaySubject<T> ha più di un modo per risolvere questo problema. Per questo esempio, vedremo due esempi che utilizzano vincoli di dimensione e tempo per limitare il valore memorizzato.
Nel primo caso, useremo la dimensione della cache per limitare il valore della cache. <T>Il costruttore di ReplaySubject fornisce un sovraccarico, che accetta un intero che rappresenta la dimensione del buffer della cache (numero massimo di elementi). Nel nostro esempio, cambiiamo il codice per limitare la dimensione della cache a 1.
Nota come usiamo <T>il sovraccarico del costruttore di ReplaySubject per fornire la dimensione della Cache come 1. Questo limita la cache e garantisce che solo un elemento venga memorizzato nella cache e sostituito con un nuovo elemento non appena pubblicato. L'impatto del cambiamento è mostrato di seguito.
Un altro modo per limitare la cache è limitare il tempo dell'oggetto memorizzato, o in altre parole, fornire un tempo di scadenza per l'oggetto memorizzato.
Scriviamo codice per illustrare quell'esempio.
Similmente al codice precedente, usiamo<T> il sovraccarico del costruttore ReplaySubject per specificare il tempo di scadenza degli elementi nella cache. Per dimostrare il nostro caso, abbiamo introdotto un ritardo tra la pubblicazione dei valori.
Poiché ci vogliono ben 1200 ms prima che l'osservatore si abboni, qualsiasi elemento che superi i 1000 ms verrà rimosso dalla cache. In questo esempio, questo farà rimuovere il valore 1 dalla cache e non verrà riprodotto agli abbonati in ritardo. Come mostrato nella figura sottostante.
<T>Ci sono altri sovraccarichi per ReplaySubject che offrono maggiore flessibilità e ottimizzano i valori nella cache, ma per gli esempi manterremo i due esempi già trattati sopra.
Comportamento Oggetto<T>
BehaviourSubject <T>è molto simile a<T> ReplaySubject in quanto aiuta a mettere in cache i valori. Ma c'è una differenza significativa. BehaviourSubject<T> memorizza nella cache solo l'ultimo valore pubblicato. Prima di approfondire, scriviamo un po' di codice.
Se il<T> BehaviourSubject memorizza nella cache solo un singolo valore (che è l'ultimo noto), in cosa si differenzia da un ReplaySubject di dimensione 1<T>? Il diagramma seguente riflette chiaramente la situazione del codice sopra.
Tuttavia, questo non è del tutto vero. Ci sono due differenze importanti da comprendere qui. La prima è la presenza di default. Si noti che nel codice sopra, <T>forniamo il valore 0 come predefinito nel costruttore di BehaviourSubject. Se non esiste alcun valore nella cache (o in altre parole, nessun dato è stato pubblicato prima che l'osservatore si sottoscrivesse), il valore predefinito verrà restituito. Questo è diverso da ReplaySubject, che ha una dimensione 1<T>, che non ha alcun valore. Il codice seguente e una rappresentazione visiva della sequenza dimostrano questo comportamento.
La seconda differenza è come si<T> comportano BehaviorSubject e<T> ReplaySubject quando si abbonano a una sequenza completata. Quando ti iscrivi dopo il completamento, il BehaviorSubject <T> non avrà alcun valore, come mostrato nel codice qui sotto.
Gli abbonati sono garantiti di non ricevere alcun valore perché gli abbonamenti avvengono dopo il completamento.
Tuttavia, <T>questo è il caso di ReplaySubject . Non vi è alcuna garanzia che l'osservatore non riceverà alcun valore, come mostrato nel codice sottostante.
Come mostrato nel codice sopra, la cache ha una dimensione 1 e anche se l'abbonamento viene richiamato dopo il completamento della chiamata, la cache rimarrà (fino a quando non sarà soddisfatta la condizione di scadenza), quindi in questo caso verrà ricevuto l'ultimo valore pubblicato.
AsyncSubject<T>
<T>AsyncSubject è l'ultimo fratello del Subject che esploreremo in questo <T>articolo, ed è molto simile ai due precedenti (ReplaySubject e BehaviourSubject) in quanto memorizza anche i risultati. Ma di nuovo, c'è una differenza significativa. AsyncSubject pubblica l'ultimo valore memorizzato nella cache solo se la sequenza è contrassegnata come completa <T> (memorizza nella cache solo un valore, l'ultimo valore).
Considera il seguente codice.
Questo genererà un valore per l'osservatore che la sequenza viene contrassegnata come ultimo valore pubblicato prima del completamento - valore 4. Come mostrato nella figura sottostante.
Ma cosa succede se saltiamo la chiamata che segna la sequenza come completa? Commentiamo la frase e riproviamo.
Questo non genera dati per l'osservatore perché AsyncSubject<T> pubblica i risultati solo dopo che la sequenza è stata contrassegnata come completata.
Questa è una differenza significativa che chiunque utilizzi <T>AsyncSubject dovrebbe tenere a mente.
conclusione
Questo articolo mostra <T>le differenze tra i vari fratelli del Soggetto e alcune delle sue variazioni. Spesso è utile essere consapevoli di queste sottili differenze, poiché possono manifestare comportamenti diversi da quelli che ti aspettavi se non te ne rendi conto.
Link originale:Il login del link ipertestuale è visibile.
|