La gestione strutturata degli errori di runtime

Nel post sulla gestione degli errori di runtime con Visual Basic .NET (a cui si rimanda: link) abbiamo visto la sintassi del gestore di eccezioni Try..Catch..Finally e un esempio di utilizzoL’esempio, molto semplice, utilizzava un solo gestore di eccezioni con tre blocchi Catch. Il massimo dell’efficienza nella gestione degli errori di runtime, però, la si ottiene con un uso strutturato di questo gestore. Il linguaggio Visual Basic .NET, infatti, offre anche la possibilità di utilizzare più gestori di eccezioni nidificati (o annidati) (combinati uno dentro l’altro, ndr).

VB.NET

Si ricorda che quando in un gestore di eccezioni si utilizzano più blocchi Catch, al verificarsi di un errore di runtime in un’istruzione del blocco Try, l’esecuzione del programma viene sospesa su quell’istruzione e i blocchi Catch vengono esaminati, nello stesso ordine in cui compaiono all’interno del gestore, alla ricerca del primo fra questi che presenti un filtro che accetti l’eccezione che è stata generata:

  • quando questo blocco Catch viene trovato, prima di tutto vengono eseguite le istruzioni presenti al suo interno (che devono rimediare all’errore che si è verificato), i blocchi Catch successivi vengono saltati e l’esecuzione del programma continua, eseguendo prima il blocco Finally, se è presente, e poi proseguendo oltre l’istruzione End Try;
  • quando questo blocco Catch, invece, non viene trovato, l’esecuzione del programma riprende nel blocco Try dal punto in cui era stata sospesa, con tutte le conseguenze che il verificarsi dell’errore comporta e, se queste non sono “distruttive” per l’applicazione (crash del programma), l’esecuzione del programma continua, eseguendo prima il blocco Finally, se è presente, e poi proseguendo oltre l’istruzione End Try.

Quello appena descritto è l’uso più semplice che si può fare di un gestore di eccezioni. Vediamo ora un semplice esempio con un uso strutturato di questo gestore.

Esempio
Riprendiamo l’algoritmo del ‘Maggiore di una sequenza di numeri’ che abbiamo progettato in un altro post (a cui si rimanda: link) e del quale riportiamo qui la tabella delle variabili e il flow chart dell’algoritmo finale:

maggioreNumeri

maggiore_di_una_ sequenza_02

traduciamolo in Visual Basic:

gui_maggiore_sequenza

controlli_maggiore_sequenza

L’interfaccia è stata progettata prevedendo che il programma prima si procuri il dato su quanti sono i numeri che compongono la sequenza, attraverso la casella di testo inserita nel Form, e con il clic sul bottone, poi, inizi a  procurarsi i numeri della sequenza, uno alla volta, utilizzando una InputBox.

Procedendo con la codifica in Visual Basic dell’algoritmo finale così come è stato progettato, si ottiene il seguente codice:

Poiché il compilatore non segnala alcun errore e, inoltre, la progettazione dell’algoritmo non contiene errori logici, siamo certi che il programma sin qui scritto è esente sia da errori formali, sia da errori logici. In fase di debugging però si può rilevare che ci sono alcune righe che possono dare origine ad errori di runtime.

In particolare le istruzioni delle righe 6 e 11 possono generare delle eccezioni appartenenti alla classe FormatException. In entrambe le righe, infatti, viene eseguita una conversione di tipo implicita di un dato di input, dal formato stringa ad un formato numerico: se l’utente inserisce un input non valido (una sequenza non interpretabile come un numero, perché per esempio contenente delle lettere – ndr), allora l’esecuzione della conversione di tipo determina un errore che genera appunto un’eccezione di formato non valido.

Più in particolare, la riga 6 prevede che l’utente inserisca un numero intero (di tipo Integer), mentre la riga 11 si aspetta di ricevere in input un numero reale (di tipo Double); pertanto per generare delle eccezioni più specifiche, converrebbe rendere esplicite quelle conversioni di tipo implicite, per esempio con l’uso del metodo Parse, come di seguito mostrato:

Entrambe le linee di codice individuate come possibili fonti di errori di runtime, devono essere inserite in un blocco Try di un gestore di eccezioni, in modo da catturare le eventuali eccezioni generate.

Questa volta, però, non possiamo utilizzare un solo gestore di eccezioni, infatti:

  • nel caso della riga 6, all’uscita dal blocco Catch del gestore di eccezioni, il flusso di esecuzione del programma deve riprendere dall’istruzione End Sub in modo da dare all’utente la possibilità di correggere l’input inserito nella casella di testo e rifare clic sul bottone per far ripartire l’esecuzione della Sub;
  • nel caso della riga 11, invece, all’uscita dal blocco Catch del gestore di eccezioni, il flusso del programma deve riprendere rientrando nel ciclo di ripetizione, senza eseguire l’iterazione che ha determinato il verificarsi dell’errore di runtime, offrendo così all’utente la possibilità di correggere l’ultimo numero di input della sequenza che ha generato l’errore, mantenendo salvo anche il regolare flusso del programma.

Ciò può essere ottenuto utilizzando due gestori di eccezioni distinti e annidati, come mostrato di seguito:

Con quanto scritto sopra si ottiene proprio quello che volevamo, poiché, si fa notare, ciascun blocco Catch fa riferimento sempre al proprio blocco Try di appartenenza, nel senso che può intercettare solo le eccezioni generate da errori di runtime che si verificano in corrispondenza di istruzioni inserite nel blocco Try di appartenenza.

Per capire più facilmente qual è il blocco Try a cui ciacun blocco Catch appartiene, conviene mantenere visivamente separate le diverse strutture Try..Catch, dando a ciascuna una diversa indentazione (tabulazione, ndr). Un grosso aiuto in tal senso viene fornito dall’editor dell’IDE di Visual Basic che automaticamente allinea ciascun blocco Catch con il relativo blocco Try di appartenenza.

Conclusione

Grazie alla possibilità di utilizzare i gestori di eccezioni in modo strutturato, si riesce ad avere una gestione delle eccezioni molto efficiente e completa, con la possibilità di fornire all’utente informazioni puntuali circa l’errore che si è verificato e le azioni da intraprendere per correggerlo.

P.S. Nell’esempio precedente, utilizzando un ulteriore gestore di eccezioni, si potrebbe pensare di gestire in maniera specifica anche il clic sul bottone ‘Annulla’ delle InputBox utilizzate per prelevare gli input dei numeri della sequenza, in modo da determinare, per esempio, l’annullamento dell’operazione di input dell’intera sequenza. Un’idea per poterlo fare, potrebbe essere quella di generare un’eccezione in corrispondenza della pressione del bottone ‘Annulla’ dell’InputBox. Ma questo lo vedremo in un altro post (Generae eccezioni).