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