Partager via


Utiliser des exceptions

En C#, les erreurs dans le programme au moment de l’exécution sont propagées via le programme à l’aide d’un mécanisme appelé exceptions. Les exceptions sont levées par le code qui rencontre une erreur et interceptées par le code qui peut corriger l’erreur. Les exceptions peuvent être levées par le runtime .NET ou par du code dans un programme. Une fois qu’une exception est levée, elle se propage dans la pile d'appels jusqu’à ce qu’une instruction catch relative à l’exception soit trouvée. Les exceptions non interceptées sont gérées par un gestionnaire d’exceptions générique fourni par le système qui affiche une boîte de dialogue.

Les exceptions sont représentées par des classes dérivées de Exception. Cette classe identifie le type d’exception et contient des propriétés qui ont des détails sur l’exception. La levée d’une exception implique la création d’une instance d’une classe dérivée d’exception, la configuration facultative des propriétés de l’exception, puis la levée de l’objet à l’aide du throw mot clé. Par exemple:

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

Une fois qu’une exception est levée, le runtime vérifie l’instruction actuelle pour voir s’il se trouve dans un try bloc. Si c’est le cas, tous les catch blocs associés au try bloc sont vérifiés pour voir s’ils peuvent intercepter l’exception. Catch les blocs spécifient généralement des types d’exceptions ; si le type du catch bloc est le même type que l’exception ou une classe de base de l’exception, le catch bloc peut gérer la méthode. Par exemple:

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

Si l’instruction qui lève une exception n’est pas dans un bloc try ou si le bloc try qui l’entoure n’a pas de bloc catch correspondant, le runtime vérifie la méthode appelée pour une instruction try et des blocs catch. Le runtime continue jusqu’à la pile appelante, à la recherche d’un bloc catch compatible. Une fois le bloc trouvé et exécuté, le contrôle est transféré à l'instruction qui suit ce bloc.

Une try instruction peut contenir plusieurs catch blocs. La première catch instruction qui peut gérer l’exception est exécutée ; toutes les instructions suivantes catch , même si elles sont compatibles, sont ignorées. Ordonnez les blocs catch du plus spécifique (ou plus dérivé) au moins spécifique. Par exemple:

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");
        }
    }
}

Avant l’exécution du catch bloc, le runtime vérifie les finally blocs. Les blocs Finally permettent au programmeur de nettoyer tout état ambigu qui pourrait subsister après l’abandon d’un bloc try, ou de libérer les ressources externes (telles que les handles graphiques, les connexions aux bases de données ou les flux de fichiers) sans attendre que le garbage collector du runtime finalise les objets. Par exemple:

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");
    }
}

Si WriteByte() a levé une exception, le code du deuxième try bloc qui tente de rouvrir le fichier échouerait si file.Close() n’est pas appelé, et le fichier resterait verrouillé. Étant donné que finally les blocs sont exécutés même si une exception est levée, le finally bloc dans l’exemple précédent permet de fermer correctement le fichier et permet d’éviter une erreur.

Si aucun bloc compatible catch n’est trouvé sur la pile des appels après la levée d’une exception, l’une des trois choses se produit :

  • Si l’exception se trouve dans un finaliseur, le finaliseur est abandonné et le finaliseur de base, le cas échéant, est appelé.
  • Si la pile des appels contient un constructeur statique ou un initialiseur de champ statique, une TypeInitializationException est levée, et l’exception d’origine est affectée à la propriété InnerException de la nouvelle exception.
  • Si le début du thread est atteint, le thread est arrêté.