Le espressioni lambda nella gestione degli eventi con Visual Basic 2010
a cura di Alessandro Del Sole (requisiti: conoscenza generica delle espressioni lambda)Introduzione
Fin dalla versione 2008, Visual Basic si è arricchito di un'importante aggiunta costituita dalle espressioni lambda che, in parole molto povere, sono dei metodi anonimi, generabili al volo, che agiscono come delegate.
A dire il vero, nella versione 2008 le lambda in Visual Basic erano piuttosto incomplete, poiché potevano essere solo di tipo Function (quindi dovevano necessariamente restituire un valore) e su una sola riga.
Come invece abbiamo già discusso in un precedente articolo, in Visual Basic 2010 si fa un passo avanti. Vengono infatti introdotte le espressioni lambda multilinea, quindi metodi generabili all'interno di altri metodi, e statement lambdas ossia espressioni lambda di tipo Sub che non restituiscono valori.
L'unione di queste due caratteristiche può portarci a scrivere codice più raffinato anche nella gestione degli eventi ed è, questo, l'argomento del presente articolo.
Il codice sorgente a corredo è scaricabile dall'area Download di VB T&T.Dichiarare eventi e gestori: il modo classico
Supponiamo di avere la necessità di implementare un Timer. Al passare dell'intervallo specificato, vogliamo che venga intrapresa una determinata azione, che nel nostro caso è la semplice visualizzazione di un messaggio nella finestra della Console. La soluzione è abbastanza elementare: ricordando che in Visual Basic possiamo utilizzare l'operatore AddressOf per specificare un gestore di evento, ma che possiamo alternativamente dichiarare una variabile come WithEvents invece di usare AddHandler, le possibilità che avremmo nel modo classico sono le seguenti:'Alternativa classica 1 Class ClassicEvents 'Dichiaro la variabile come WithEvents Private WithEvents aTimer As New Timers.Timer Sub New() Me.aTimer.Interval = 2000 Me.aTimer.Enabled = True End Sub 'Scrivo il gestore di evento, specificando la clausola Handles Private Sub aTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) _ Handles aTimer.Elapsed Console.WriteLine(e.SignalTime.ToLongTimeString) End Sub End Class'Alternativa classica 2 Class ClassicEvents Private aTimer As New Timers.Timer Sub New() Me.aTimer.Interval = 2000 'Specifico, tramite AddressOf, il metodo che dovrà gestire 'l'evento interessato da AddHandler AddHandler aTimer.Elapsed, AddressOf aTimer_Elapsed Me.aTimer.Enabled = True End Sub 'Senza Handles, ho usato AddHandler Private Sub aTimer_Elapsed(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs) Console.WriteLine(e.SignalTime.ToLongTimeString) End Sub End ClassNulla di particolarmente difficile. Vediamo come possiamo cambiare le cose con le espressioni lambda.
Utilizzare espressioni lambda come gestore di eventi
Una considerazione molto comune sulle espressioni lambda è la seguente: si possono utilizzare laddove è richiesto un delegate. Ciò significa che un delegate è completamente sostituibile da un'espressione lambda. Ciò premesso, ecco come potremmo riscrivere la dichiarazione AddHandler e il gestore di evento utilizzando le statement lambda di VB 2010:Class EventsWithLambdas Private aTimer As New Timers.Timer Sub New() Me.aTimer.Interval = 2000 AddHandler aTimer.Elapsed, Sub(sender As Object, e As Timers.ElapsedEventArgs) Console.WriteLine(e.SignalTime.ToLongTimeString) End Sub Me.aTimer.Enabled = True End Sub End ClassCome si può osservare, invece di scrivere un gestore separato a cui si punta tramite AddressOf, inserisco il mio metodo anonimo direttamente nella dichiarazione AddHandler, specificando anche gli elementi richiesti dalla firma del delegate. A proposito di delegate, il secondo argomento dell'operatore AddHandler è proprio un delegate a cui si punta tramite AddressOf. Poiché la lambda viene correttamente accettata, senza AddressOf, è evidente la conferma dell'affermazione secondo cui una lambda si può utilizzare sempre laddove occorra un delegate.
Sfruttare i relaxed delegates
Sempre in questo contesto, possiamo sfruttare un'altra caratteristica del linguaggio introdotta già da VB 2008, ossia i relaxed delegates. Di cosa si tratta? Nel rimandarvi alla documentazione MSDN per i dettagli, essenzialmente possiamo pensare che gli oggetti sender ed e all'interno delle firme dei gestori non sempre vengono utilizzati e, quindi, se non li utilizziamo, possiamo fare a meno di indicarli. Ecco quindi come possiamo riscrivere il gestore di evento unendo statement lambda e relaxed delegate nel caso in cui non volessimo usare le informazioni di tipo ElapsedEventArgs:Class EventsWithShortLambdas Private aTimer As New Timers.Timer Sub New() Me.aTimer.Interval = 2000 'Qui è un relaxed delegate AddHandler aTimer.Elapsed, Sub() Console.WriteLine("E' passato un altro intervallo:" & _ Date.Now.ToLongTimeString) End Sub Me.aTimer.Enabled = True End Sub End ClassUn modo conciso ed elegante per scrivere un gestore di evento assolutamente funzionante.
Un esempio più utile: generare controlli a runtime
Questo tipo di tecnica può trovare particolare utilità quando dobbiamo generare dei controlli a runtime, più che altro perché rende più leggibile il codice. Ipotizziamo quindi di avere un'applicazione WPF e di voler generare un pulsante a runtime, del quale dobbiamo anche gestire l'evento Click. Ecco quindi come si può raggiungere questo obiettivo utilizzando la tecnica finora descritta:Class MainWindow Dim myButton As Button Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) _ Handles Me.Loaded Me.myButton = New Button With myButton .Width = 100 .Height = 30 .Content = "Runtime button" .Name = "RuntimeButton" End With 'Potrei anche scrivere Sub(sender As Object, e As RoutedEventArgs) ma non lo uso, 'quindi: relaxed delegate AddHandler myButton.Click, Sub() MessageBox.Show("You clicked") End Sub 'Grid1 è il nome della Grid principale nella finestra Me.Grid1.Children.Add(myButton) End Sub End ClassIn questo modo, al clic sul pulsante verrà visualizzato il messaggio specificato, esattamente come sarebbe accaduto se avessimo scritto un gestore di evento al di fuori di AddHandler.
Conclusioni
Le espressioni lambda si addicono a molti utilizzi nelle applicazioni .NET e la gestione degli eventi è solo uno dei possibili usi. Permettono di scrivere codice elegante e anche più ordinato, considerato che evitano di riempire il file di codice di metodi separati. Per ulteriori informazioni potete contattarmi al mio indirizzo di posta elettronica oppure visitare il mio blog.