1. Introduction
Dès que ton programme devient un peu plus complexe que Hello, world!, tu te retrouves vite à devoir gérer différents types d’erreurs de façon différente. Imagine : tu bosses avec des fichiers, du réseau, une BDD — et pour chaque cas d’erreur, il faut réagir autrement. Par exemple, si un fichier manque, tu peux proposer à l’utilisateur d’en choisir un autre, et si c’est une erreur réseau — retenter l’opération ou afficher un message sympa genre "Vérifie le câble, peut-être que le chat l’a encore bouffé".
En C#, pour ça, on utilise plusieurs blocs catch à la suite. Chaque bloc est spécialisé sur son type d’exception (et ses enfants).
Structure d’un catch multiple
try
{
// Ici — du code qui peut lancer différentes exceptions
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"Fichier non trouvé : {ex.Message}");
}
catch (IOException ex) // attrape toutes les erreurs d’entrée/sortie, sauf FileNotFoundException
{
Console.WriteLine($"Erreur d’entrée-sortie : {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Un truc s’est mal passé : {ex.Message}");
}
Important ! Le compilateur parcourt les blocs de haut en bas et prend le premier qui correspond au type. Si un FileNotFoundException est attrapé, il ne va pas plus loin dans la chaîne.
Illustration de la "chaîne"
| Type d’exception | Quel bloc va l’attraper ? |
|---|---|
| FileNotFoundException | Premier catch |
| IOException (autre) | Deuxième catch |
| ArgumentException | Troisième (général) catch |
Pourquoi il ne faut pas tout mélanger ?
Le "piège" le plus large — genre catch (Exception) — mets-le toujours en dernier, sinon il va "gober" toutes les exceptions trop tôt, et les catch spécialisés ne seront jamais atteints.
C’est comme désactiver tous les détecteurs d’incendie d’un immeuble parce qu’un a réagi à un grille-pain cramé : les prochains incendies, le système ne les verra pas.
Exemple concret
using System;
using System.IO;
class Program
{
static void Main()
{
try
{
double result = CalculateAverageAgeFromFile("users.txt");
Console.WriteLine($"Âge moyen — {result}");
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Erreur : fichier non trouvé. Vérifie le chemin.");
}
catch (FormatException ex)
{
Console.WriteLine("Erreur de données : impossible de lire l’âge.");
}
catch (Exception ex)
{
Console.WriteLine($"Autre erreur : {ex.Message}");
}
}
static double CalculateAverageAgeFromFile(string filePath)
{
// (Implémentation : lit le fichier, parse les âges, calcule la moyenne)
// ...
throw new NotImplementedException();
}
}
Ici, on sépare clairement quoi faire si le fichier n’existe pas (FileNotFoundException), et quoi faire si le fichier contient des données "foireuses" (FormatException). Tous les autres cas sont pris par le troisième bloc "de secours".
2. Filtres catch : attraper les subtilités
Les blocs catch multiples, c’est pratique, mais parfois ça ne suffit pas. Il arrive que, pour un même type d’exception, tu veuilles réagir différemment selon les circonstances.
Par exemple, si le réseau ne marche pas parce qu’il n’y a plus d’internet — on peut tenter de se reconnecter. Mais si c’est juste le serveur qui répond pas — peut-être qu’il faut afficher un autre message.
C’est là que les filtres catch entrent en jeu — une vraie super-feature de C# qui permet de filtrer non seulement par type, mais aussi par condition supplémentaire.
Syntaxe du filtre when
catch (IOException ex) when (ex.Message.Contains("pas de disque"))
{
Console.WriteLine("Oups ! On dirait que tu as retiré la clé USB.");
}
catch (IOException ex)
{
Console.WriteLine("Autre erreur d’entrée/sortie : " + ex.Message);
}
Ici, le premier bloc attrape seulement les IOException dont le message contient la phrase "pas de disque" — tout le reste va dans le deuxième bloc.
Utilisation dans la vraie vie
Les filtres sont super utiles pour gérer les erreurs réseau, quand il faut décider quoi faire selon les propriétés internes de l’exception : retenter, ou juste afficher une erreur.
Encore un exemple : imagine, dans une méthode qui parse un fichier, tu veux pas juste un FormatException "générique", mais un cas à part si c’est l’âge qui pose problème (genre si l’âge est écrit "abc" au lieu d’un nombre).
catch (FormatException ex) when (ex.Message.Contains("âge"))
{
Console.WriteLine("Erreur : impossible de lire l’âge. Vérifie les données.");
}
catch (FormatException ex)
{
Console.WriteLine("Erreur de format des données : " + ex.Message);
}
Filtres et performance
Les filtres, c’est pratique, mais rappelle-toi que la condition du filtre est évaluée avant d’entrer dans le bloc catch, donc si elle n’est pas vraie — le corps du bloc n’est même pas exécuté.
D’ailleurs, si le filtre lui-même lance une exception (genre dans ton expression when tu fais une division par zéro) — cette exception ne sera jamais attrapée par ce bloc catch. Faut faire gaffe à ça.
3. Combiner plusieurs catch et des filtres
Imagine que dans ton projet tu dois gérer non seulement le type d’exception, mais aussi son état interne. Par exemple, avec une IOException, tu veux un comportement différent si c’est un problème d’accès ou si le disque est plein.
try
{
File.AppendAllText("log.txt", "Nouvelle entrée\n");
}
catch (IOException ex) when (ex.Message.Contains("Plus d’espace"))
{
Console.WriteLine("Erreur : Le disque est plein !");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Erreur : pas les droits pour écrire le fichier. Lance en tant qu’admin.");
}
catch (IOException ex)
{
Console.WriteLine("Autre erreur disque : " + ex.Message);
}
Ici, on utilise à la fois un filtre et une séparation par type d’erreur. Cette flexibilité est super utile quand tu développes une appli complexe où il faut réagir de façon informative aux plantages fréquents.
4. Particularités et "pièges" des filtres et des catch multiples
- Le filtre catch peut utiliser la variable d’exception (ex), mais ne peut pas la modifier.
- Si tu lances une exception dans when, elle ne sera jamais "attrapée" par ce même catch — elle remonte la pile comme si le filtre n’existait pas.
- La logique du filtre fait partie du contrat : ton collègue doit savoir que toutes les IOException ne seront pas attrapées — seulement si la condition est vraie.
- Si tu utilises un seul catch pour toute la méthode, mais avec un filtre dedans, il y a un risque de rater des exceptions "en trop". Donc, si tu doutes — préfère des blocs séparés et explicites.
- Dans les grosses applis, tu peux utiliser les filtres pour centraliser la logique, par exemple, ne logger que les erreurs critiques.
5. Scénarios pour les entretiens et le boulot réel
Connaître les filtres et les catch multiples, ce n’est pas juste pour faire joli ou avoir une bonne note en clean code. C’est une vraie compétence qu’on peut te demander en entretien, surtout si tu postules pour un poste où tu dois maintenir une grosse appli complexe et distribuée.
Exemples de questions :
- Comment, en C#, gérer différents scénarios d’erreur de lecture de fichier, pour afficher un message différent selon la situation (pas de fichier, disque occupé, données corrompues) ?
- Comment ne pas "étouffer" une erreur importante si tu mets un bloc général catch (Exception) ?
- À quoi sert le filtre catch ? Est-ce qu’on peut mettre un throw dedans ?
GO TO FULL VERSION