Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Gestione degli errori

Guida al Visual Basic .NET

Capitolo 32° - Gestione degli errori

<< Precedente Prossimo >>

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.

<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.