Condividi tramite


Usare le eccezioni

In C# gli errori nel programma in fase di esecuzione vengono propagati tramite il programma usando un meccanismo denominato eccezioni. Le eccezioni vengono generate dal codice che rileva un errore e intercettate dal codice in grado di correggere l'errore. Le eccezioni possono essere generate dal runtime .NET o dal codice in un programma. Una volta che un'eccezione viene lanciata, essa si propaga lungo lo stack di chiamate fino a quando non viene trovata un'istruzione catch per tale eccezione. Le eccezioni non rilevate vengono gestite da un gestore di eccezioni generico fornito dal sistema che visualizza una finestra di dialogo.

Le eccezioni sono rappresentate da classi derivate da Exception. Questa classe identifica il tipo di eccezione e contiene proprietà con dettagli sull'eccezione. La generazione di un'eccezione comporta la creazione di un'istanza di una classe derivata da eccezioni, la configurazione facoltativa delle proprietà dell'eccezione e la generazione dell'oggetto tramite la throw parola chiave . Per esempio:

class CustomException : Exception
{
    public CustomException(string message)
    {
    }
}
private static void TestThrow()
{
    throw new CustomException("Custom exception in TestThrow()");
}

Dopo la generazione di un'eccezione, il runtime controlla l'istruzione corrente per verificare se si trova all'interno di un try blocco. Se lo è, tutti i blocchi catch associati al blocco try vengono controllati per stabilire se possono intercettare l'eccezione. Catch i blocchi specificano in genere tipi di eccezione; se il tipo del catch blocco è lo stesso tipo dell'eccezione o una classe base dell'eccezione, il catch blocco può gestire il metodo . Per esempio:

try
{
    TestThrow();
}
catch (CustomException ex)
{
    System.Console.WriteLine(ex.ToString());
}

Se l'istruzione che genera un'eccezione non si trova all'interno di un blocco try oppure se il blocco try che lo racchiude non ha un blocco catch corrispondente, il runtime controlla nel metodo chiamante una dichiarazione try e blocchi catch. Il runtime continua a risalire lo stack di chiamate, cercando un blocco compatibile catch. Dopo che il catch blocco è stato trovato ed eseguito, il controllo passa all'istruzione successiva dopo quel catch blocco.

Un'istruzione try può contenere più blocchi catch . Viene eseguita la prima catch istruzione in grado di gestire l'eccezione. Tutte le istruzioni seguenti catch , anche se compatibili, vengono ignorate. Ordinare i blocchi catch dal più specifico (o più derivato) al meno specifico. Per esempio:

using System;
using System.IO;

namespace Exceptions
{
    public class CatchOrder
    {
        public static void Main()
        {
            try
            {
                using (var sw = new StreamWriter("./test.txt"))
                {
                    sw.WriteLine("Hello");
                }
            }
            // Put the more specific exceptions first.
            catch (DirectoryNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            // Put the least specific exception last.
            catch (IOException ex)
            {
                Console.WriteLine(ex);
            }
            Console.WriteLine("Done");
        }
    }
}

Prima dell'esecuzione del catch blocco, il runtime verifica la presenza di finally blocchi. Finally I blocchi consentono al programmatore di pulire qualsiasi stato ambiguo che potrebbe essere lasciato da un blocco interrotto try o di rilasciare qualsiasi risorsa esterna (ad esempio handle grafici, connessioni di database o flussi di file) senza attendere che il Garbage Collector nel runtime finalizzi gli oggetti. Per esempio:

static void TestFinally()
{
    FileStream? file = null;
    //Change the path to something that works on your machine.
    FileInfo fileInfo = new System.IO.FileInfo("./file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        file?.Close();
    }

    try
    {
        file = fileInfo.OpenWrite();
        Console.WriteLine("OpenWrite() succeeded");
    }
    catch (IOException)
    {
        Console.WriteLine("OpenWrite() failed");
    }
}

Se WriteByte() è stata generata un'eccezione, il codice nel secondo try blocco che tenta di riaprire il file avrà esito negativo se file.Close() non viene chiamato e il file rimarrà bloccato. Poiché finally i blocchi vengono eseguiti anche se viene generata un'eccezione, il finally blocco nell'esempio precedente consente la chiusura corretta del file e consente di evitare un errore.

Se non viene trovato alcun blocco compatibile catch nello stack di chiamate dopo la generazione di un'eccezione, si verifica una delle tre operazioni seguenti:

  • Se l'eccezione si trova all'interno di un finalizzatore, il finalizzatore viene interrotto e viene chiamato il finalizzatore di base, se presente.
  • Se lo stack di chiamate contiene un costruttore statico o un inizializzatore di campo statico, viene generata un'eccezione TypeInitializationException con l'eccezione originale assegnata alla InnerException proprietà della nuova eccezione.
  • Se viene raggiunto l'inizio del thread, il thread viene terminato.