|
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 Try
Tra 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 Module
Ora 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 Module
Provando 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 Finally
Il 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 Try
Le 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 personalizzate
Ammettiamo 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 Module
Come 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.
|
|