Partager via


Créer et lever des exceptions

Les exceptions sont utilisées pour indiquer qu’une erreur s’est produite lors de l’exécution du programme. Les objets d’exception qui décrivent une erreur sont créés, puis lancés avec la instruction ou l’expressionthrow. Le runtime recherche ensuite le gestionnaire d’exceptions le plus compatible.

Les programmeurs doivent lever des exceptions lorsqu’une ou plusieurs des conditions suivantes sont remplies :

  • La méthode ne peut pas terminer sa fonctionnalité définie. Par exemple, si un paramètre d’une méthode a une valeur non valide :

    static void CopyObject(SampleClass original)
    {
        _ = original ?? throw new ArgumentException("Parameter cannot be null", nameof(original));
    }
    
  • Un appel inapproprié à un objet est effectué, en fonction de l’état de l’objet. Un exemple peut être d’essayer d’écrire dans un fichier en lecture seule. Dans les cas où l'état d'un objet n'autorise pas une opération, lèvez une instance de InvalidOperationException ou un objet basé sur une dérivation de cette classe. Voici un exemple d’une méthode qui lève un objet InvalidOperationException :

    public class ProgramLog
    {
        FileStream logFile = null!;
        public void OpenLog(FileInfo fileName, FileMode mode) { }
    
        public void WriteLog()
        {
            if (!logFile.CanWrite)
            {
                throw new InvalidOperationException("Logfile cannot be read-only");
            }
            // Else write data to the log and return.
        }
    }
    
  • Lorsqu’un argument à une méthode provoque une exception. Dans ce cas, l’exception d’origine doit être interceptée et une ArgumentException instance doit être créée. L’exception d’origine doit être passée au constructeur du ArgumentException comme le paramètre InnerException :

    static int GetValueFromArray(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    

    Remarque

    L’exemple précédent montre comment utiliser la InnerException propriété. C’est intentionnellement simplifié. Dans la pratique, vous devez vérifier qu’un index est dans les limites avant de l’utiliser. Vous pouvez utiliser cette technique d’encapsulation d’exception lorsqu’un membre d’un paramètre lève une exception que vous ne pouviez pas anticiper avant d’appeler ce membre.

Les exceptions contiennent une propriété nommée StackTrace. Cette chaîne contient le nom des méthodes sur la pile des appels actuelle, ainsi que le nom de fichier et le numéro de ligne où l’exception a été levée pour chaque méthode. Un objet StackTrace est créé automatiquement par le CLR (Common Language Runtime) à partir du point de l’instruction throw, de sorte que les exceptions doivent être levées à partir du point où la trace de la pile doit commencer.

Toutes les exceptions contiennent une propriété nommée Message. Cette chaîne doit être définie pour expliquer la raison de l’exception. Les informations sensibles à la sécurité ne doivent pas être placées dans le texte du message. En plus de Message, ArgumentException contient une propriété nommée ParamName qui doit être définie sur le nom de l’argument qui a provoqué la levée de l’exception. Dans le cas d’une méthode setter de propriété, ParamName doit être défini en tant que value.

Les méthodes public et protected doivent lever des exceptions chaque fois qu’elles ne parviennent pas à remplir leurs fonctions habituelles. La classe d’exception levée est l’exception la plus spécifique disponible qui correspond aux conditions d’erreur. Ces exceptions doivent être documentées dans le cadre de la fonctionnalité de classe, et les classes dérivées ou mises à jour de la classe d’origine doivent conserver le même comportement pour la compatibilité descendante.

Éléments à éviter lors de la levée d’exceptions

La liste suivante identifie les pratiques à éviter lors de la levée d’exceptions :

  • N’utilisez pas d’exceptions pour modifier le flux d’un programme dans le cadre de l’exécution ordinaire. Utilisez des exceptions pour signaler et gérer les conditions d’erreur.
  • Les exceptions ne doivent pas être retournées en tant que valeur ou paramètre de retour au lieu d’être levées.
  • Ne lancez pas intentionnellement System.Exception, System.SystemException, System.NullReferenceException ou System.IndexOutOfRangeException depuis votre propre code source.
  • Ne créez pas d’exceptions qui peuvent être levées en mode débogage, mais pas en mode mise en production. Pour identifier les erreurs d’exécution pendant la phase de développement, utilisez Debug.Assert à la place.

Exceptions dans les méthodes de renvoi de tâches

Les méthodes déclarées avec le async modificateur ont des considérations particulières lorsqu’il s’agit d’exceptions. Les exceptions lancées dans une méthode async sont stockées dans la tâche renvoyée et n'apparaissent pas tant que, par exemple, la tâche n'est pas attendue. Pour plus d’informations sur les exceptions stockées, consultez Exceptions asynchrones.

Nous vous recommandons de valider les arguments et de lever toutes les exceptions correspondantes, telles que ArgumentException et ArgumentNullException, avant d’entrer les parties asynchrones de vos méthodes. Autrement dit, ces exceptions de validation doivent apparaître de façon synchrone avant le démarrage du travail. L’extrait de code suivant montre un exemple dans lequel, si les exceptions sont levées, les ArgumentException exceptions apparaissent de façon synchrone, tandis que celles-ci InvalidOperationException sont stockées dans la tâche retournée.

// Non-async, task-returning method.
// Within this method (but outside of the local function),
// any thrown exceptions emerge synchronously.
public static Task<Toast> ToastBreadAsync(int slices, int toastTime)
{
    if (slices is < 1 or > 4)
    {
        throw new ArgumentException(
            "You must specify between 1 and 4 slices of bread.",
            nameof(slices));
    }

    if (toastTime < 1)
    {
        throw new ArgumentException(
            "Toast time is too short.", nameof(toastTime));
    }

    return ToastBreadAsyncCore(slices, toastTime);

    // Local async function.
    // Within this function, any thrown exceptions are stored in the task.
    static async Task<Toast> ToastBreadAsyncCore(int slices, int time)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        // Start toasting.
        await Task.Delay(time);

        if (time > 2_000)
        {
            throw new InvalidOperationException("The toaster is on fire!");
        }

        Console.WriteLine("Toast is ready!");

        return new Toast();
    }
}

Définir des classes d’exception

Les programmes peuvent lever une classe d’exception prédéfinie dans l’espace System de noms (sauf si indiqué précédemment) ou créer leurs propres classes d’exception en dérivant de Exception. Les classes dérivées doivent définir au moins trois constructeurs : un constructeur sans paramètre, un qui définit la propriété de message, et un qui définit à la fois les propriétés Message et InnerException. Par exemple:

[Serializable]
public class InvalidDepartmentException : Exception
{
    public InvalidDepartmentException() : base() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
}

Ajoutez de nouvelles propriétés à la classe d’exception lorsque les données qu’ils fournissent sont utiles pour résoudre l’exception. Si de nouvelles propriétés sont ajoutées à la classe d’exception dérivée, ToString() elles doivent être remplacées pour retourner les informations ajoutées.

Spécification du langage C#

Pour plus d’informations, consultez Exceptions et instruction Throw 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