Gestion avancée des erreurs en C# : du try/catch de base aux modèles industriels
\n\nLa gestion des erreurs n'est pas simplement un blocage des exceptions, mais un art de concevoir des systèmes fiables. En C# (.NET), les développeurs se limitent souvent à la construction try/catch, mais la plateforme offre des mécanismes bien plus puissants. Dans cet article, nous analyserons les techniques avancées : des filtres d'exceptions aux modèles de reprise et de transmission du contexte d'erreur.
1. Filtres d'exceptions : la précision plutôt que les béquilles
\n\nLes filtres d'exceptions (Exception Filters), apparus en C# 6.0, permettent de spécifier une condition pour entrer dans un bloc catch sans relancer l'exception. C'est beaucoup plus propre que l'approche classique avec if à l'intérieur du bloc.
try\n{\n // Code pouvant lever une exception\n var result = ProcessData(input);\n}\ncatch (CustomException ex) when (ex.ErrorCode == 1001)\n{\n // Traite uniquement les erreurs avec le code 1001\n Log.Error($"Erreur critique : {ex.Message}");\n throw; // Relance si nécessaire\n}\ncatch (CustomException ex) when (ex.IsRecoverable)\n{\n // Erreur récupérable — on journalise et on continue\n Log.Warning($"Erreur récupérable : {ex.Message}");\n RetryOperation();\n}\n\nAvantages :
\n- \n
- Pas besoin de
throwsupplémentaire pour le filtrage. \n - Améliore la lisibilité et les performances (la pile d'appels n'est pas altérée).
- Possibilité de combiner plusieurs blocs
catchavec différentes conditions.
2. Modèle de reprise (Retry) et Polly
\n\nToutes les erreurs ne sont pas fatales. Pour les pannes temporaires (problèmes réseau, verrous de base de données), utilisez le modèle de tentatives répétées. Les outils intégrés de C# sont limités, c'est pourquoi la bibliothèque Polly est recommandée.
\n\nusing Polly;\nusing Polly.Retry;\n\n// Définition de la politique de tentatives\nAsyncRetryPolicy retryPolicy = Policy\n .Handle<HttpRequestException>() // Traite uniquement les erreurs HTTP\n .WaitAndRetryAsync(\n 3, // Nombre de tentatives\n retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // Délai exponentiel\n onRetry: (exception, timeSpan, retryCount, context) =>\n {\n Log.Warning($"Tentative {retryCount} échouée. Nouvel essai dans {timeSpan.Seconds} sec.");\n });\n\n// Application de la politique\nawait retryPolicy.ExecuteAsync(async () =>\n{\n var response = await httpClient.GetAsync("https://api.example.com/data");\n response.EnsureSuccessStatusCode();\n});\n\nImportant : N'utilisez pas de tentatives infinies. Définissez toujours un nombre maximum de tentatives et utilisez un Circuit Breaker pour protéger le système des défaillances en cascade.
\n\n3. Transmission correcte des exceptions : throw vs throw ex
\n\nL'une des erreurs les plus fréquentes est la perte de la pile d'appels. La différence entre throw et throw ex est cruciale pour le débogage.
try\n{\n // ...\n}\ncatch (Exception ex)\n{\n // INCORRECT : on perd la pile d'origine\n // throw ex; \n\n // CORRECT : on conserve la pile\n throw;\n\n // OU : on ajoute du contexte sans perdre l'original\n // throw new ApplicationException("Erreur dans le module X", ex);\n}\n\nSi vous devez ajouter du contexte, utilisez ExceptionDispatchInfo (de l'espace de noms System.Runtime.ExceptionServices) pour conserver précisément la pile lors du transfert entre threads.
ExceptionDispatchInfo.Capture(exception).Throw(); // Conserve la pile d'origine\n\n4. Exceptions personnalisées et contexte d'erreur
\n\nLes exceptions standard ne portent souvent pas de contexte métier. Créez vos propres classes d'exceptions avec des propriétés supplémentaires.
\n\npublic class PaymentProcessingException : Exception\n{\n public int OrderId { get; }\n public dec