Les exceptions peuvent en contenir d’autres, comment gérer cette hiérarchie simplement ?
Une exception peut en cacher une autre !
A l’instar des trains, une exception peut en cacher une autre. Ce qui de plus en informatique est récursif puisque cette autre exception peut elle-même en cacher une autre… etc.
Une exception est facile à traiter, mais dès lors qu’on cherche à savoir si elle contient une autre exception (et ainsi de suite) les choses se compliquent et souvent cette hiérarchie est ignorée par le développeur. Pourtant – au moins pour une trace de débogue par exemple – connaitre facilement toutes les exceptions qui s’enchainent est crucial, car souvent le message intéressant se trouve caché dans cette chaîne.
Aplatir la hiérarchie
Joli rêve d’anarchiste ! Ici il ne s’agira que de rendre facilement manipulable la structure chainée des exceptions en la linéarisation sous forme d’une liste.
L’idée est plutôt simple et on peut régler le problème par une méthode d’extension comme celle-ci :
using System;
using System.Collections.Generic;
namespace Utils
{
public static class DebugExtensions
{
public static IEnumerable<Exception> FlattenHierarchy(this Exception ex)
{
if (ex == null) throw new ArgumentNullException("ex");
var innerException = ex;
do
{
yield return innerException;
innerException = innerException.InnerException;
}
while (innerException != null);
}
}
}
Plus de chainage à gérer dans le code mais une simple liste d’exceptions qu’on peut interroger et traiter facilement avec Linq.
Exemple fictif où l’exception est “aplatie”, chaque exception (1er niveau ou interne) étant envoyée à un gestionnaire de message d’erreur global :
// ...
catch (Exception ex)
{
ex.FlattenHierarchy().ToList()
.ForEach(x => AddErrorToGlobalList("EmailError", x.Message));
}
On peut aussi filtrer aisément les exceptions ce qui rend le traitement plus exhaustif que de se limiter à tester le type ou le message de premier niveau :
if (exception.FlattenHierarchy().Where(e => e.Message == "Disk crash!").Any()) ...
Conclusion
Rien de bien sorcier, mais encore faut-il penser à ajouter cette méthode d’extension dans toutes les applications car ne gérer que le premier niveau n’est pas tout à fait suffisant lorsqu’on souhaite cibler une condition particulière sans mettre des try/catch trop génériques (ce qui n’est pas un bon réflexe, à trop masquer toutes les erreurs ont loupe même en phase de test des bogues qui surgiront plus tard et plus gravement en production !).
Bon débogue…
Stay Tuned !