Guida al Visual Basic .NET
Capitolo 32° - Gestione degli errori
Fino ad ora, nello scrivere il codice degli esempi, ho sempre (o quasi sempre) supposto che l'utente inserisse dati coerenti e corretti. Al massimo, ho inserito qualche costrutto di controllo per verificare che tutto andasse bene. Infatti, per quello che abbiamo visto fino ad ora, c'erano solo due modi per evitare che il programma andasse in crash o producesse output privi di senso: scrivere del codice a prova di bomba (e questo, garantisco, non è sempre possibile) o controllare, prima di eseguire le operazioni, che tutti i dati fossero perfettamente coerenti con il problema da affrontare. Ahim?, non è sempre possibile agire in questo modo: ci sono certi casi in cui né l'uno né l'altro metodo sono efficaci. E di questo posso fornire subito un esempio lampante: ammettiamo di aver scritto un programma che esegua la divisione tra due numeri. Molto banale come codice. Chiediamo all'utente i suddetti dati con Console.ReadLine, controlliamo che il secondo sia diverso da 0 (proprio per evitare un errore a runtime) e in questo caso stampiamo il risultato. Ma... se l'utente inserisse, ad esempio, una lettera anziché un numero, o per sbaglio o per puro sadismo? Beh, qualcuno potrà pensare "Usiamo TryCast", tuttavia TryCast, essendo una riedizione di DirectCast, agisce solo verso tipi reference e Int32 è un tipo base. Qualcun altro, invece, potrebbe proporre di usare TryParse, ma abbiamo già rilevato come la funzione Parse sia di vedute ristrette. In definitiva, non abbiamo alcun modo di controllare prima se il dato immesso o no sia realmente coerente con ciò che stiamo chiedendo all'utente. Possiamo sapere se il dato non è coerente solo quando si verifica l'errore, ma in questo caso non possiamo permetterci che il programma vada in crash per una semplice distrazione. Dovremo, quindi, gestire l'errore. In .NET quelli che finora ho chiamato "errori" si dicono, più propriamente, Eccezioni e sono anch'esse rappresentate da una classe: la classe base di tutte le eccezioni è System.Exception, da cui derivano tutte le varianti per le specifiche eccezioni (ad esempio divisione per zero, file inesistente, formato non valido, eccetera...). Accanto a queste, esiste anche uno specifico costrutto che serve per gestirle, e prende il nome di Try. Ecco la sua sintassi: Try 'Codice che potrebbe generare l'eccezione Catch [Variabile] As [Tipo Eccezione] 'Gestisce l'eccezione [Tipo Eccezione] End TryTra Try e Catch viene scritto il codice incriminato, che potrebbe eventualmente generare l'errore che noi stiamo tentando di rintracciare e gestire. Nello specifico, quando accade un avvenimento del genere, si dice che il codice "lancia" un'eccezione, poiché la parola chiave usata per generarla, come vedremo, è proprio Throw (= lanciare). Ora, passatemi il paragone che sto per fare, forse un po' fantasioso: il metodo in questione è come una fionda che scaglia un sassolino - un pacchetto di informazioni che ci dice tutto sul perchè e sul per come è stato generato quello specifico errore. Ora, se questo sassolino viene intercettato da qualcosa (dal blocco catch), possiamo evitare danni collaterali, ma se niente blocca la sua corsa, ahimé, dovremmo ripagare i vetri rotti a qualcuno. Ecco un esempio: Module Module1 Sub Main() Dim a, b As Single 'ok controlla se a e b sono coerenti Dim ok As Boolean = False Do 'Tenta di leggere i numeri da tastiera Try Console.WriteLine("Inserire due numeri non nulli: ") a = Console.ReadLine b = Console.ReadLine 'Se il codice arriva fino a questo punto, significa 'che non si sono verificate eccezioni ok = True Catch Ex As InvalidCastException 'Se, invece, il programma arriva in questo blocco, 'vuol dire che abbiamo "preso" (catch) un'eccezione 'di tipo InvalidCastException, che è stata '"lanciata" dal codice precedente. Tutti i dati 'relativi a quella eccezione sono ora conservati 'nella variabile Ex. 'Possiamo accedervi oppure no, come in questo caso, 'ma sono in ogni caso informazioni utili, come 'vedremo fra poco Console.WriteLine("I dati inseriti non sono numeri!") 'I dati non sono coerenti, quindi ok = False ok = False End Try 'Richiede gli stessi dati fino a che non si tratta 'di due numeri Loop Until ok 'Esegue il controllo su b e poi effettua la divisione If b <> 0 Then Console.WriteLine("{0} / {1} = {2}", a, b, a / b) Else Console.WriteLine("Divisione impossibile!") End If Console.ReadKey() End Sub End ModuleOra potreste anche chiedervi "Come faccio a sapere quale classe rappresenta quale eccezione?". Beh, in genere, si mette un blocco Try dopo aver notato il verificarsi dell'errore e quindi dopo aver letto il messaggio di errore che contiene anche il nome dell'eccezione. In alternativa si può specificare come tipo semplicemente Exception, ed in quel caso verranno catturate tutte le eccezioni generate, di qualsiasi tipo. Ecco una variante dell'esempio precedente: Module Module1 Sub Main() 'a e b sono interi short, ossia possono assumere 'valori da -32768 a +32767 Dim a, b As Int16 Dim ok As Boolean = False Do Try Console.WriteLine("Inserire due numeri non nulli: ") a = Console.ReadLine b = Console.ReadLine ok = True Catch Ex As Exception 'Catturiamo una qualsiasi eccezione e stampiamo il 'messaggio 'ad essa relativo. Il messaggio è contenuto nella 'proprietà Message dell'oggetto Ex. Console.WriteLine(Ex.Message) ok = False End Try Loop Until ok If b <> 0 Then Console.WriteLine("{0} / {1} = {2}", a, b, a / b) Else Console.WriteLine("Divisione impossibile!") End If Console.ReadKey() End Sub End ModuleProvando ad inserire un numero troppo grande o troppo piccolo si otterrà "Overflow di un'operazione aritmetica."; inserendo una stringa non convertibile in numero si otterrà "Cast non valido dalla stringa [stringa] al tipo 'Short'". Questa versione, quindi, cattura e gestisce ogni possibile eccezione. Ricordate che è possibile usare anche più clausole Catch in un unico blocco Try, ad esempio una per ogni eccezione diversa. Clausola FinallyIl costrutto Try è costituito da un blocco Try e da una o più clausole Catch. Tuttavia, opzionalmente, è possibile specificare anche un'ulteriore clausola, che deve essere posta dopo tutti i Catch: Finally. Finally dà inizio ad un altro blocco di codice che viene sempre eseguito, sia che si generi un'eccezione, sia che non se ne generi alcuna. Il codice ivi contenuto viene eseguito comunque dopo il try e il catch. Ad esempio, assumiamo di avere questo blocco di codice, con alcune istruzioni di cui non ci interessa la natura: marchiamo le istruzioni con delle lettere e ipotizziamo che la D generi un'eccezione:Try A B C D E F Catch Ex As Exception G H Finally I L End TryLe istruzioni eseguite saranno: A B C 'Eccezione: salta nel blocco Catch G H 'Alla fine esegue comunque il Finally I L Lanciare un'eccezione e creare eccezioni personalizzateAmmettiamo ora di aver bisogno di un'eccezione che rappresenti una particolare circostanza che si verifica solo nle nostro programma, e di cui non esiste un corrispettivo tra le eccezioni predefinite del Framework. Dovremo scrivere una nuova eccezione. Per far ciò, bisogna semplicemente dichiarare una nuova classe che erediti dalla classe Exeption:Module Module1 'Questa classe rappresenta l'errore lanciato quando una 'password imessa è sbagliata. Per convenzione, tutte le 'classi che rappresentano un'eccezione devono terminare 'con la parola "Exception" Class IncorrectPasswordException Inherits System.Exception 'Eredita da Exception 'Queste proprietà ridefiniscono quelle della classe 'Exception tramite polimorfismo, perciò sono 'dichiarate Overrides 'Sovrascrive il messaggio di errore Public Overrides ReadOnly Property Message() As String Get Return "La password inserita ? sbagliata!" End Get End Property 'Modifica il link di aiuto Public Overrides Property HelpLink() As String Get Return "http://totem.altervista.org" End Get Set(ByVal Value As String) MyBase.HelpLink = value End Set End Property 'Il resto dei membri di Exception sono molto importanti 'e vengono inizializzati con dati prelevati tramite 'Reflection (ultimo argomento di questa sezione), perciò 'è conveniente non modificare altro. Potete 'semmai aggiungere qualche membro End Class Sub Main() Dim Pass As String = "b7dha90" Dim NewPass As String Try Console.WriteLine("Inserire la password:") NewPass = Console.ReadLine If NewPass <> Pass Then 'Lancia l'eccezione usando la keyword Throw Throw New IncorrectPasswordException End If Catch IPE As IncorrectPasswordException 'Visualizza il messaggio Console.WriteLine(IPE.Message) 'E il link d'aiuto Console.WriteLine("Help: " & IPE.HelpLink) End Try Console.ReadKey() End Sub End ModuleCome si è visto nell'esempio, lanciare un'eccezione è molto semplice: basta scrivere Throw, seguito da un oggetto Exception valido. In questo caso abbiamo creato l'oggetto IncorrectPasswordException nello stessa linea di codice in cui l'abbiamo lanciato.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|