Gestion des exceptions (Guide de programmation C#)

Un bloc try est utilisé par les programmeurs C# pour partitionner du code susceptible d’être affecté par une exception. Des blocs catch associés sont utilisés pour gérer les exceptions générées. Un bloc finally contient du code qui s’exécute dans tous les cas, qu’une exception soit levée ou non dans le bloc try (il peut s’agir par exemple de la libération des ressources allouées dans le bloc try). Un bloc try doit être associé à un ou plusieurs blocs catch, à un bloc finally, ou aux deux.

Les exemples suivants montrent une instruction try-catch, une instruction try-finally et une instruction try-catch-finally.

try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}
try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}
try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks
    // goes here.
}

Un bloc try sans bloc catch ou finally provoque une erreur du compilateur.

Blocs catch

Un bloc catch peut spécifier le type d’exception à intercepter. La spécification de type est appelée filtre d’exception. Le type d’exception doit être dérivé de Exception. En général, ne spécifiez pas Exception comme filtre d’exception sauf si vous savez comment gérer toutes les exceptions susceptibles d’être levées dans le bloc try ou si vous avez inclus une instruction throw à la fin de votre bloc catch.

Vous pouvez chaîner plusieurs blocs catch avec des classes d’exception différents. Les blocs catch sont évalués de haut en bas dans votre code, mais un seul bloc catch est exécuté pour chaque exception levée. Le premier bloc catch qui spécifie le type exact ou une classe de base de l’exception levée est exécuté. Si aucun bloc catch ne spécifie une classe d’exception correspondant, un bloc catch qui n’a pas de type est sélectionné, s’il y en a un dans l’instruction. Lors du positionnement des blocs catch, il est important de placer en premier les classes d’exception les plus spécifiques (c’est-à-dire les plus dérivés).

Interceptez les exceptions quand les conditions suivantes sont remplies :

  • Vous savez pourquoi l’exception a été levée et vous pouvez implémenter une récupération spécifique, par exemple inviter l’utilisateur à entrer un nouveau nom de fichier quand vous interceptez un objet FileNotFoundException.
  • Vous pouvez créer et lever une nouvelle exception plus spécifique.
    int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    
  • Vous voulez traiter partiellement une exception avant de la transmettre en vue d’un traitement supplémentaire. Dans l’exemple suivant, un bloc catch est utilisé pour ajouter une entrée à un journal d’erreurs avant de relever l’exception.
    try
    {
        // Try to access a resource.
    }
    catch (UnauthorizedAccessException e)
    {
        // Call a custom error logging procedure.
        LogError(e);
        // Re-throw the error.
        throw;
    }
    

Vous pouvez également indiquer des filtres d’exception pour ajouter une expression booléenne à une clause catch. Les filtres d’exception indiquent qu’une clause catch spécifique ne correspond que lorsque cette condition est vraie. Dans l’exemple suivant, les deux clauses catch utilisent la même classe d’exception, mais une condition supplémentaire est vérifiée pour créer un message d’erreur différent :

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException e) when (index < 0) 
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be negative.", e);
    }
    catch (IndexOutOfRangeException e)
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be greater than the array size.", e);
    }
}

Un filtre d’exception qui retourne toujours false peut être utilisé pour examiner toutes les exceptions sans les traiter. Une pratique courante consiste à enregistrer les exceptions :

public class ExceptionFilter
{
    public static void Main()
    {
        try
        {
            string? s = null;
            Console.WriteLine(s.Length);
        }
        catch (Exception e) when (LogException(e))
        {
        }
        Console.WriteLine("Exception must have been handled");
    }

    private static bool LogException(Exception e)
    {
        Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
        Console.WriteLine($"\tMessage: {e.Message}");
        return false;
    }
}

La méthode LogException retourne toujours false, aucune clause catch utilisant ce filtre d’exception ne correspond. La clause catch peut être générale et utiliser System.Exception. Les clauses ultérieures peuvent traiter des classes d’exception plus spécifiques.

Blocs Finally

Un bloc finally vous permet de nettoyer les actions qui sont exécutées dans un bloc try. S’il est présent, le bloc finally s’exécute en dernier, après le bloc try et tout bloc catch mis en correspondance. Un bloc finally s’exécute toujours, qu’une exception soit levée ou non ou même si aucun bloc catch correspondant au type d’exception n’est trouvé.

Le bloc finally peut être utilisé pour libérer des ressources telles que des flux de fichiers, des connexions de base de données et des handles graphiques, sans attendre que le récupérateur de mémoire dans le runtime finalise les objets.

Dans l’exemple suivant, le bloc finally est utilisé pour fermer un fichier ouvert dans le bloc try. Notez que l’état du handle de fichier est vérifié avant la fermeture du fichier. Si le bloc try ne peut pas ouvrir le fichier, le handle de fichier a encore la valeur null et le bloc finally n’essaie pas de le fermer. Au lieu de cela, si le fichier est ouvert avec succès dans le bloc try, le bloc finally ferme le fichier ouvert.

FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    file?.Close();
}

Spécification du langage C#

Pour plus d’informations, consultez Exceptions et Instruction try dans la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.

Voir aussi