Gestire gli errori di runtime con Visual Basic .NET

Gli errori di runtime sono quegli errori di programmazione che non possono essere rilevati dal compilatore durante la fase di compilazione e che si manifestano solo durante la fase di esecuzione del programma e solo in alcune circostanze (per saperne di più: Errori di programmazione).

VB.NET

Alcuni esempi di errori di runtime sono: il verificarsi di una divisione per 0; l’utilizzo di un percorso file non valido o che fa riferimento ad una periferica che in un certo momento risulta scollegata; il verificarsi di un overflow su un tipo di dato da parte di un’operazione aritmetica; un’operazione di casting non valido (ad esempio l’assegnazione di una stringa di testo non interpretabile come un numero ad una variabile di tipo numerico). Si tratta di errori pericolosi perché se non gestiti dall’applicazione, possono determinare il blocco inaspettato del programma (crash dell’applicazione). Prevedere questo tipo di errori non è facile e Visual Basic .NET, per rendere più semplice la loro gestione, mette a disposizione del programmatore degli strumenti avanzati.

Visual Basic .Net supporta una gestione strutturata delle eccezioni, che permette di creare programmi con gestori di errori efficaci e completi, che permettono di porre rimedio a queste situazioni anomale, come ad esempio a quelle dovute ad input non validi o errori di sistema, permettendo nel contempo di fornire all’utente facilmente dei messaggi opportuni.

Sintassi del gestore di errori

La gestione avanzata degli errori di runtime può essere realizzata utilizzando il gestore delle eccezioni Try…Catch…Finally con la seguente sintassi:

Nel blocco Try si inseriscono le istruzioni che possono determinare potenziali errori di runtime, nei blocchi Catch si inseriscono le istruzioni che devono gestire gli errori.
Se durante l’esecuzione delle istruzioni del blocco Try viene generata un’eccezione, per esempio per il verificarsi di un un errore di runtime, Visual Basic sospende l’esecuzione del programma e esamina i blocchi Catch presenti all’interno della struttura Try…End Try, alla ricerca di un blocco Catch che presenti una condizione di filtro delle eccezioni che sia compatibile con l’eccezione che è stata generata. Se questa condizione viene trovata, il controllo passa alla prima riga di codice di quel blocco Catch e gli altri blocchi Catch vengono saltati. Diversamente, se tra l’eccezione intercettata nel blocco Try e i filtri dei blocchi Catch non viene rilevata alcuna corrispondenza, invece, viene generato l’errore (per approfondimenti: Gestione strutturata degli errori di runtime).

Il blocco Finally non è obbligatorio e in esso si inseriscono le istruzioni che si vuole vengano eseguite dal programma sempre, prima di uscire dal gestore delle eccezioni, indipendentemente dal fatto che si verifichino o meno delle eccezioni o da quali eccezioni si verificano nel gestore. Il codice incluso in un blocco Finally, addirittura, viene eseguito anche se in un blocco Try o Catch si incontra un’istruzione Return e viene sempre eseguito per ultimo, prima di uscire dal gestore delle eccezioni.

NOTE

  1. Le istruzioni Try ed End Try sono obbligatorie; le istruzioni Catch e Finally sono opzionali, nel codice però è necessario includere almeno una fra le due.
  2. È possibile specificare più istruzioni Catch in modo che ogni blocco Catch gestisca un errore specifico.
  3. I blocchi Catch di un gestore di eccezioni Try…End Try vengono esaminati da Visual Basic in sequenza, sempre partendo dal primo, e fermandosi al primo di essi che presenta una condizione di filtro delle eccezioni compatibile con l’errore che si è verificato; in tal caso i successivi  della sequenza vengono saltati.
  4. Le variabili locali di un blocco Try (sono quelle dichiarate all’interno del blocco, ndr) non sono disponibili in un blocco Catch in quanto rappresentano blocchi separati. Pertanto, se si desidera utilizzare una variabile in più blocchi, bisogna dichiararla al di fuori della struttura Try…Catch…Finally.

I filtri del blocco Catch

Le eccezioni intercettate da uno specifico blocco Catch possono essere filtrate (selezionate, ndr) secondo due modalità fondamentali:

  1. in base alla classe di appartenenza dell’eccezione;
  2. in base a una fissata espressione condizionale.

Vediamo cosa vuol dire con un semplice esempio.

Esempio

Problema. Scrivere in Visual Basic un programma che calcola il quoziente e il resto della divisione intera fra due interi forniti in input e che gestisca in modo strutturato i possibili errori di runtime.

Interfaccia grafica

vb-runtime-error-table

Nel programma da realizzare è possibile prevedere le seguenti eccezioni:

  1. divisione per zero;
  2. un input non valido da parte dell’utente: in input sono ammessi solo numeri interi.

Codice Visual Basic .NET

Nota alla riga n. 21
Nell’esempio questo filtro è stato utilizzato solo a scopo didattico, per mostrare l’uso di un filtro in base a una condizione. In particolare, Visual Basic .NET fornisce un modo più sicuro per intercettare l’eccezione della  ‘Divisione per zero’, che consiste nell’utilizzare la classe specifica per questo tipo di eccezione, DivideByZeroException.

Nel codice precedente:

  • il primo blocco Catch è un esempio di filtro in base alla classe di appartenenza dell’eccezione; esso infatti intercetterà solo le eccezioni che rientrano nella classe FormatException, che è il tipo di eccezione specificato dopo la parola chiave As;
  • il secondo blocco Catch è un esempio di filtro in base ad un’espressione condizionale, quella specificata dopo la parola chiave When;
  • il terzo blocco Catch, invece, fa riferimento alla classe generica delle eccezioni, Exception, pertanto è un blocco generico che può essere utile per intercettare tutte le eccezioni non previste, diverse da quelle intercettate dai blocchi precedenti.

Non sempre, infatti, è possibile prevedere tutti gli errori che possono verificarsi.

NOTE

  • L’ordine con cui si scrivono i blocchi Catch non è ininfluente.
  • Attenzione a sistemare il blocco delle eccezioni generico sempre per ultimo, prima del blocco Finally, altrimenti intercetterebbe anche le eccezioni che si vorrebbe, invece, venissero intercettate dagli eventuali blocchi con filtri più specifici sistemati dopo.

Nel caso particolare dell’esempio precedente, infatti, un errore di ‘Formato non valido’ non facendo eseguire l’assegnazione sulla variabile divisore, determinerebbe per essa un valore nullo e se il blocco Catch della ‘Divisione per zero’ venisse sistemato come primo blocco, esso intercetterebbe anche l’eccezione del ‘Formato non valido’ (con il verificarsi dell’eccezione ‘Formato non valido’ le assegnazioni non vengono eseguite e il compilatore VB .NET pone a zero le variabili numeriche che non vengono inizializzate esplicitamente, ndr).

NOTA

Come mostrato nell’esempio, è possibile ottenere informazioni circa l’eccezione che si è verificata utilizzando la proprietà Message dell’oggetto dell’eccezione generata, essa fornisce una stringa di messaggio che descrive l’errore.