presenta Come, quando e perché usare le Reactive Extensions Raffaele Rialdi - MVP Developer Security @raffaeler raffaeler@vevy.com www.wpc2015.it info@wpc2015.it - +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 info@wpc2015.it - +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 3 1 2 20 30 10 Reactive Linq 10 1 20 2 30 3 source.where(x => x % 2 == 0) observable.where(x => x % 2 == 0) 2, 10, 20, 30 10 20 2 30 www.wpc2015.it info@wpc2015.it - +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 info@wpc2015.it - +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 oppure var disposable = observable.subscribe( m => Console.WriteLine(m), // OnNext e => Console.WriteLine(e.Message), // OnError () => Console.WriteLine("OnCompleted")); // OnCompleted var observer = GetObserver<long>(); var disposable = observable.subscribe(observer); 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 info@wpc2015.it - +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)); } Come fosse un timer Observable.Interval(TimeSpan.FromSeconds(1)); Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)); Da un set di dati: Observable.Range(1, 10); Molti altri. 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); }; return () => { _timer.enabled = false; _timer.dispose(); }; www.wpc2015.it info@wpc2015.it - +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 info@wpc2015.it - +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 info@wpc2015.it - +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)); Consuma dati for (int i = 0; i < 10; i++) { subject.onnext(i.tostring()); } 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 info@wpc2015.it - +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 info@wpc2015.it - +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 info@wpc2015.it - +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 info@wpc2015.it - +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 info@wpc2015.it - +39 02 365738.11 16
Domande e Risposte Q & A www.wpc2015.it info@wpc2015.it - +39 02 365738.11 - #wpc15it 17
OverNet Education info@overneteducation.it 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 info@wpc2015.it - +39 02 365738.11 - #wpc15it 19