Gestion des exceptions en C# (try, catch, finally)
Apprenez à gérer les exceptions en C# avec les blocs try, catch et finally afin de traiter les erreurs de manière sûre avec exemples.
En C#, la gestion des erreurs permet d’intercepter et de contrôler les situations inattendues pendant l’exécution du programme.
Cela évite un arrêt brutal et permet d’afficher un message approprié ou d’exécuter une action alternative.
La gestion des erreurs utilise ensemble les blocs try, catch et finally.
Utilisation de try-catch
Le bloc try contient du code pouvant générer une exception.
Si une erreur survient, le bloc catch l’intercepte.
try
{
int nombre = int.Parse("abc"); // Conversion invalide
Console.WriteLine("Nombre: " + nombre);
}
catch (FormatException ex)
{
Console.WriteLine("Erreur : format de nombre invalide.");
}
// Sortie :
Erreur : format de nombre invalide.
Plusieurs blocs catch
Il est possible d’utiliser plusieurs catch pour différents types d’erreurs.
Chaque type d’exception peut ainsi être traité séparément.
try
{
int[] nombres = { 1, 2, 3 };
Console.WriteLine(nombres[5]); // Dépassement d’indice
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Erreur : indice de tableau hors limites.");
}
catch (Exception ex)
{
Console.WriteLine("Erreur inattendue : " + ex.Message);
}
Advanced Error Handling: Exception Filters (when)
En C#, les filtres d’exception permettent d’ajouter une condition à un
catch en utilisant le mot-clé when.
Cela permet de gérer une exception uniquement si une condition spécifique est remplie.
Le principal avantage des filtres d’exception est que la condition est évaluée
avant d’entrer dans le bloc catch.
Si la condition retourne false, le runtime ignore ce bloc et continue
à rechercher une autre clause catch correspondante.
Cela permet :
- Une gestion des erreurs plus claire et plus lisible
- Un contrôle plus précis de la logique des exceptions
- De réduire le besoin d’instructions
ifimbriquées dans les blocscatch
Exemple : Gestion des erreurs HTTP selon le code de statut
Dans les applications réelles, les requêtes HTTP retournent souvent différents codes de statut. Grâce aux filtres d’exception, nous pouvons traiter certains codes de manière spécifique tout en gardant un code structuré et lisible.
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
try
{
HttpResponseMessage response =
await client.GetAsync("https://api.example.com/users/1");
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (HttpRequestException ex)
when (ex.StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine("Ressource non trouvée (404).");
}
catch (HttpRequestException ex)
when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
Console.WriteLine("Accès non autorisé (401). Vérifiez vos identifiants.");
}
catch (HttpRequestException ex)
{
Console.WriteLine("Une erreur HTTP générale s’est produite.");
Console.WriteLine($"Code de statut : {ex.StatusCode}");
}
}
}
Dans cet exemple :
- Si l’API retourne
404 Not Found, un message spécifique est affiché. - Si l’API retourne
401 Unauthorized, le traitement est effectué séparément. - Les autres erreurs HTTP sont gérées dans le bloc général
HttpRequestException.
- La condition
whenest évaluée avant d’entrer dans le bloccatch. - Si la condition est
false, le runtime ignore cette clausecatch. - Les filtres d’exception ont été introduits avec C# 6 et sont largement utilisés dans le développement backend moderne.
Rethrowing Exceptions: throw; vs throw ex;
Une exception interceptée dans un bloc catch peut être relancée.
Cependant, throw; et throw ex; ne se comportent pas de la même manière.
La différence principale réside dans la préservation de la stack trace. La stack trace indique la chaîne d’appels de méthodes qui a conduit à l’erreur ainsi que la ligne exacte où elle s’est produite à l’origine.
-
throw;Relance l’exception tout en conservant sa source d’origine (le numéro de ligne et la méthode où l’erreur est apparue). Lors du débogage, il est possible de voir précisément où l’exception a réellement commencé. C’est la méthode correcte et recommandée. -
throw ex;Réinitialise la stack trace à cette ligne et donne l’impression que l’erreur s’est produite dans le bloccatch. La véritable origine de l’erreur (par exemple une méthode plus profonde) est alors perdue, ce qui rend le débogage plus difficile.
Exemple
using System;
class Program
{
static void Main()
{
try
{
MethodA();
}
catch (Exception ex)
{
Console.WriteLine("Erreur enregistrée.");
// Utilisation correcte :
throw;
// Utilisation incorrecte :
// throw ex;
}
}
static void MethodA()
{
MethodB();
}
static void MethodB()
{
throw new InvalidOperationException("Une erreur s’est produite.");
}
}
Dans cet exemple, si throw; est utilisé, l’exception apparaîtra
dans la stack trace comme provenant de MethodB.
Si throw ex; est utilisé à la place, l’exception semblera
provenir du bloc catch, et la chaîne d’appels originale sera perdue.
- Si une exception doit être relancée, il faut toujours privilégier
throw;. throw ex;complique le débogage car il altère la stack trace d’origine.- Le mot-clé
throwpeut également être utilisé pour lancer des exceptions personnalisées.
Custom Exceptions
Dans les applications réelles, toutes les erreurs ne doivent pas être représentées
par des types d’exception généraux tels que Exception
ou d’autres exceptions intégrées. Pour exprimer plus clairement
les violations des règles métier (business logic) et les problèmes spécifiques au domaine,
il est recommandé de créer des classes d’exception personnalisées.
Cette approche :
- Permet de distinguer plus clairement les différents types d’erreurs
- Offre une gestion plus spécifique dans les blocs
catch - Fournit davantage d’informations contextuelles pour la journalisation et l’analyse
- Améliore la lisibilité et la maintenabilité du code
Exemple
// 1. Définition des exceptions personnalisées
public class ProjectNotFoundException : Exception
{
public ProjectNotFoundException(string message) : base(message) { }
}
public class ProjectIsCanceledException : Exception
{
public int ProjectId { get; }
public string ProjectName { get; }
public ProjectIsCanceledException(int projectId, string projectName)
: base($"Project '{projectName}' (Id: {projectId}) is canceled and cannot be processed.")
{
ProjectId = projectId;
ProjectName = projectName;
}
}
// 2. Utilisation
public void ProcessProject(Project project)
{
if (project == null)
{
throw new ProjectNotFoundException("Le projet n’est pas enregistré dans le système.");
}
if (project.Status == ProjectStatus.Canceled)
{
throw new ProjectIsCanceledException(project.Id, project.Name);
}
// ...
}
Dans cet exemple, une ProjectNotFoundException est levée
lorsque le projet n’existe pas dans le système.
Si le projet est annulé, une
ProjectIsCanceledException est levée.
Cette exception contient non seulement un message d’erreur,
mais aussi les valeurs ProjectId et ProjectName,
ce qui rend la journalisation et l’analyse des erreurs plus pertinentes.
- Les exceptions personnalisées représentent généralement des violations de règles métier ou des problèmes liés au domaine.
- La convention de nommage suit généralement le modèle
SomethingException. - Des propriétés supplémentaires peuvent être ajoutées afin de fournir davantage d’informations contextuelles.
Bloc finally
Le bloc finally est toujours exécuté, qu’une erreur survienne ou non.
Il est souvent utilisé pour libérer des ressources comme fermer des fichiers ou des connexions.
try
{
Console.WriteLine("Ouverture du fichier...");
throw new Exception("Fichier introuvable !");
}
catch (Exception ex)
{
Console.WriteLine("Erreur : " + ex.Message);
}
finally
{
Console.WriteLine("Fermeture du fichier...");
}
// Sortie :
Ouverture du fichier...
Erreur : Fichier introuvable !
Fermeture du fichier...
Lancer des exceptions personnalisées
Vous pouvez définir vos propres conditions d’erreur et lancer des exceptions avec throw.
static void Diviser(int a, int b)
{
if (b == 0)
throw new DivideByZeroException("Erreur : division par zéro !");
Console.WriteLine("Résultat : " + (a / b));
}
static void Main()
{
try
{
Diviser(10, 0);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Erreur : " + ex.Message);
}
}
// Sortie :
Erreur : division par zéro !
En résumé
try: Contient le code susceptible de lever des exceptions.catch: Intercepte et gère les exceptions lorsqu’elles se produisent.finally: S’exécute toujours, généralement pour le nettoyage.- Plusieurs blocs
catchpermettent de gérer différents types d’exceptions séparément. - Les filtres d’exception (
when) permettent une gestion conditionnelle des exceptions. throw;préserve la stack trace d’origine lors de la relance d’une exception.throw ex;réinitialise la stack trace et doit généralement être évité.- Les exceptions personnalisées représentent des erreurs liées au domaine ou aux règles métier.
Articles connexes
Boucles en C# (for, foreach, while, do-while)
Apprenez à utiliser les boucles for, foreach, while et do-while en C# pour gérer des actions répétitives avec des exemples pratiques.
Interop en C# (Utilisation de bibliothèques C/C++)
Apprenez l’Interop en C# pour utiliser des bibliothèques C/C++, y compris P/Invoke et la gestion du code non managé.
Méthodes et utilisation des paramètres en C#
Apprenez à définir des méthodes et à utiliser des paramètres en C#, y compris les paramètres par valeur et par référence avec exemples.
Principes SOLID en C#
Application des principes SOLID en C# avec des exemples : pour un code flexible, maintenable et testable.
Structures conditionnelles en C# (if, else, switch)
Structures conditionnelles en C# : apprenez à utiliser if, else if, else et switch pour exécuter différentes actions selon les conditions.