Come, quando e perché usare le Reactive Extensions

Transcript

Come, quando e perché usare le Reactive Extensions
presenta
Come, quando e perché
usare le Reactive Extensions
Raffaele Rialdi - MVP Developer Security
@raffaeler
[email protected]
www.wpc2015.it – [email protected] - +39 02 365738.11 - #wpc15it
1
I principi della programmazione reattiva
• Composizione Asincrona di Eventi usando Sequenze Osservabili
• Quando sono utili?
 Quando è necessario processare eventi
 Più in generale quando è utile usare il pattern Publisher-Subscriber
• Il Framework.NET definisce le due interfacce base nella BCL
 IObservable<T> viene implementata dal publisher
 IObserver<T> rappresenta i subscriber
• Le Reactive eXtensions sono una delle possibili implementazioni del pattern basato
su queste due interfacce
 Non è quasi mai necessario implementare IObservable e IObserver
 Offrono degli extension methods che mimano il comportamento di Linq
 Libreria di funzionalità molto estesa
www.wpc2015.it – [email protected] - +39 02 365738.11
4
Linq + dimensione del tempo = Reactive Extensions
• Una query Linq viene eseguita in un istante "t" su un set di dati
 Al momento della query, il set di dati è invariante (non cambia durante la query stessa)
• Le reactive extensions estendono Linq aggiungendo la dimensione del tempo
 Gli eventi accadono lungo una linea temporale
• I «Marble Diagrams» permettono di visualizzare meglio questo approccio
Linq classico
1
30
3
20
Reactive Linq
2
10
10
1
20
2
30
3
observable.Where(x => x % 2 == 0)
source.Where(x => x % 2 == 0)
10
20
2
30
2, 10, 20, 30
www.wpc2015.it – [email protected] - +39 02 365738.11
5
Cos'è un «Observable»
• Rappresenta una sorgente, un generatore di sequenze osservabili
 Può essere un oggetto che implementa IObservable (raramente è utile)
 Può essere un oggetto che traduce occorrenze asincrone in IObservable
 Eventi, delegate, timer …
• La logica è totalmente capovolta
 Una sorgente dati normalmente la si accede in "pull" … si prendono attivamente i dati
 Un Observable è invece in push … i dati vengono mandati dalla sorgente verso il client
• In termini di Linq, IObservable<T> è l'equivalente di IEnumerable<T>
 È Lazy (in funzione del tempo)
 È componibile (Select, Where, GroupBy, ma anche molti altri nuovi operatori)
• Cold: sono le sequenze erogate "On Demand" (all'arrivo di un Subscriber)
• Hot: sono le sequenze che producono dati a prescindere dai Subscribers
www.wpc2015.it – [email protected] - +39 02 365738.11
6
Cos'è un «Observer»
• Rappresenta un possibile sottoscrittore dell'Observable interessato a fruire dei dati
• Il sottoscrittore è "passivo" (es: non ha possibilità di controllare la velocità dei dati)
• Il sottoscrittore comunica le tre "callback" che servono a ricevere i dati
var disposable = observable
.Subscribe(
m => Console.WriteLine(m),
e => Console.WriteLine(e.Message),
() => Console.WriteLine("OnCompleted"));
• oppure
var observer = GetObserver<long>();
var disposable = observable.Subscribe(observer);
// OnNext
// OnError
// OnCompleted
public IObserver<T> GetObserver<T>() {
return new AnonymousObserver<T>(
m => Console.WriteLine(m),
e => Console.WriteLine(e.Message),
() => Console.WriteLine("End"));
}
• La «OnError» termina la sequenza (è necessario ri-eseguire la Subscribe)
www.wpc2015.it – [email protected] - +39 02 365738.11
7
Creare un Observable
• A partire da una Action
• Mimando un "for loop"
IObservable<string> Generate() {
return Observable.Generate<int, string>(
0, // initial state
x => x < 10,
x => x + 1,
x => x.ToString(),
x => TimeSpan.FromSeconds(x));
}
IObservable<T> Gen<T>(Func<T> producer)
{
return Observable.Create<T>(obs =>
{
Timer _timer;
_timer = new Timer(500);
_timer.Enabled = true;
_timer.Elapsed += (o, e) =>
{
var t = producer();
obs.OnNext(t);
};
• Come fosse un timer
 Observable.Interval(TimeSpan.FromSeconds(1));
 Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1));
return () =>
{
_timer.Enabled = false;
_timer.Dispose();
};
• Da un set di dati:
 Observable.Range(1, 10);
});
}
• Molti altri ….
www.wpc2015.it – [email protected] - +39 02 365738.11
8
Creazione di Observable semplici
• Observable<string>()
 Viene chiamata OnCompleted
• Observable.Return(10)
 Viene chiamata OnNext(10) e OnCompleted
• Observable.Throw<long>(exception)
 Viene chiamata OnError(exception)
• Observable.Never<int>()
 Non viene chiamato nulla
www.wpc2015.it – [email protected] - +39 02 365738.11
9
Generare Observable a partire da eventi
• Observable.FromEventPattern converte un evento in Observable
IObservable<EventPattern<MouseEventArgs>> mouseDownObservable =
Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => this.MouseDown += h,
h => this.MouseDown -= h);
• Esiste anche Observable.FromEvent per eventi non-standard
 Cioè quando la firma è diversa da (object sender, EventArgs args)
• Gli eventi possono essere "projected" come fosse una query Linq
IObservable<Point> mouseDownObservable =
Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => this.MouseDown += h,
h => this.MouseDown -= h)
.Select(e => e.EventArgs.Location);
www.wpc2015.it – [email protected] - +39 02 365738.11
10
Subject<T>
public sealed class Subject<T> : ISubject<T>, ISubject<T, T>,
IObserver<T>, IObservable<T>, IDisposable
{ ... }
• Il Subject è un oggetto che si comporta sia come Observable che come Observer
 In altre parole si comporta come un Proxy
• È molto comodo per generare delle sequenze
var subject = new Subject<string>();
var observable = subject as IObservable<string>;
var disposable = observable.Subscribe(s => Console.WriteLine(s));
for (int i = 0; i < 10; i++)
{
subject.OnNext(i.ToString());
}
Consuma dati
Genera dati
• ReplaySubject<T> è una versione «cached» del Subject
 In alternativa si può usare l'extension method "Replay" su una qualsiasi Observable
• Mantiene i valori storici che verranno mandati all'Observer alla sottoscrizione
www.wpc2015.it – [email protected] - +39 02 365738.11
12
Condividere una sola Subscription con più subscribers
• L'extension method Publish
trasforma
IObservable<T> in
IConnectableObservable<T>
 I dati diventano disponibili ai
sottoscrittori attuali appena
viene invocato Connect()
 In altre parole Connect
sposta il ciclo di vita della
connessione alla sorgente
sottostante
www.wpc2015.it – [email protected] - +39 02 365738.11
13
Operatori interessanti
• Throttle, Buffer, Combine, CombineLatest, Zip, Merge, Take, Skip, TimeStamp, ...
Esempio Autocomplete
Observable.FromEventPattern<EventHandler, EventArgs>(
h => textBox1.TextChanged += h,
h => textBox1.TextChanged -= h)
.Select(e => ((TextBox)e.Sender).Text)
.Throttle(TimeSpan.FromMilliseconds(200))
.Where(t => t.Length > 2)
.DistinctUntilChanged()
.ObserveOn(this)
.Subscribe(x => Suggest(x)));
www.wpc2015.it – [email protected] - +39 02 365738.11
14
Reactive in action
• Disponibile su nuget




Rx-Main: package principale usato tipicamente nelle app server
Rx-Winforms: referenzia Rx-Main e aggiunge helpers per facilitare Control.Invoke
Rx-WPF: Come per Winforms, ma per WPF
Rx-WindowStoreApps: Idem ma per le Apps
• Disponibile per C++, Javascript, Ruby, Python, Java, etc. etc.
www.wpc2015.it – [email protected] - +39 02 365738.11
15
Scenari dinamici
• Non esiste una IObservable non tipizzata
 Negli scenari dinamici è una limitazione (anche se superabile)
• I dati che fluiscono tramite le RX possono essere trasformati "On Demand"
 Richiede la creazione dinamica delle Expression che fungono da Projection
• Il filtraggio dei dati è più efficiente se viene eseguito prima possibile
 La Where deve essere quanto più vicina alla sorgente
 La creazione della Where via Expression è una soluzione efficiente
• Se siete interessati a questi scenari, vi aspetto alla prossima sessione su
Metaprogramming in C# (sala Verde)
www.wpc2015.it – [email protected] - +39 02 365738.11
16
Domande e Risposte
Q&A
www.wpc2015.it – [email protected] - +39 02 365738.11 - #wpc15it
17
OverNet Education
[email protected]
www.overneteducation.it
Tel. 02 365738
Contatti
OverNet
Education
@overnete
www.facebook.com/OverNetEducation
www.linkedin.com/company/overnet-solutions
www.wpc2015.it
www.wpc2015.it – [email protected] - +39 02 365738.11 - #wpc15it
19