Gestion des erreurs en C# : de try/catch aux techniques professionnelles
Dans le monde du développement industriel en C# et .NET, la gestion des erreurs n'est pas simplement une construction try/catch. C'est toute une philosophie qui détermine la fiabilité, la lisibilité et la tolérance aux pannes de votre code. À un niveau avancé, le développeur ne doit pas seulement attraper les exceptions, mais aussi les concevoir, les filtrer, les journaliser et restaurer l'état de l'application de manière judicieuse. Dans cet article, nous allons analyser les aspects clés de la gestion des erreurs en C# qui vous aideront à écrire un code plus professionnel et de meilleure qualité.
1. Aller plus loin avec try/catch/finally : gestion des ressources et du contexte
La construction de base try-catch-finally est connue de tous, mais regardons-la sous un angle différent. L'objectif principal du bloc finally est la libération garantie des ressources, même en cas d'exception. Dans les versions modernes de C#, on utilise souvent using à cet effet, mais finally reste indispensable pour travailler avec des ressources non gérées ou une logique de terminaison complexe.
public void ProcessFile(string path){ FileStream? fileStream = null; try { fileStream = File.OpenRead(path); // Logique de traitement du fichier } catch (FileNotFoundException ex) { // Gestion spécifique : fichier non trouvé Log.Error($"Fichier {path} introuvable : {ex.Message}"); throw; // Relance l'exception en conservant la pile d'appels } catch (UnauthorizedAccessException) { Log.Warn($"Accès refusé au fichier : {path}"); // On peut effectuer une action alternative sans relancer l'exception return; } finally { // Libération garantie de la ressource fileStream?.Dispose(); }}Notez l'utilisation de throw; (sans argument) — c'est un point clé. Contrairement à throw ex;, la construction throw; conserve la pile d'appels d'origine, ce qui est crucial pour le débogage. N'utilisez jamais throw ex; dans des projets réels, car cela détruit les informations sur l'emplacement d'origine de l'erreur.
2. Exceptions personnalisées : concevoir un modèle d'erreur métier
Dans les grandes applications, les exceptions standard de .NET sont souvent insuffisantes. La création de vos propres classes d'exceptions permet de transmettre précisément le contexte de l'erreur et simplifie le traitement aux niveaux supérieurs. Une bonne exception personnalisée doit être sérialisable, contenir un message pertinent et transporter des informations supplémentaires (par exemple, un identifiant d'entité).
[Serializable]public class OrderProcessingException : Exception{ public int OrderId { get; } public string FaultyStep { get; }
public OrderProcessingException(int orderId, string faultyStep, string message) : base(message) { OrderId = orderId; FaultyStep = faultyStep; }
public OrderProcessingException(int orderId, string faultyStep, string message, Exception inner) : base(message, inner) { OrderId = orderId; FaultyStep = faultyStep; }
// Constructeur pour la désérialisation protected OrderProcessingException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { OrderId = info.GetInt32(nameof(OrderId)); FaultyStep = info.GetString(nameof(FaultyStep)) ?? string.Empty; }
public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { base.GetObjectData(info, context); info.AddValue(nameof(OrderId), OrderId); info.AddValue(nameof(FaultyStep), FaultyStep); }}
// Utilisationpublic void ValidateOrder(int orderId){ try {