Un tout petit article pour montrer un code qui va provoquer une grave erreur : le traitement dans le catch lève lui-même une exception.
using System; namespace ConsoleApplication5 { class Program { static void Main(string[] args) { try { throw new Exception("coucou"); } catch (Exception ex) { Console.WriteLine("Erreur : " + ex.Message + ex.InnerException != null ? ex.InnerException.ToString() : " (aucun détail)"); } } } }
Avez-vous trouvé ce qui se passait ? Personnellement, il me paraissait naturel que l’opérateur ternaire prenne la précédence sur celui de concaténation (et j’ai exprès grossi le trait avec l’indentation), mais ce n’est pas ce qui se passe, et si on veut que ça réagisse comme prévu, il faut rajouter les parenthèses comme ci-dessous :
Console.WriteLine("Erreur : " + ex.Message + (ex.InnerException != null ? ex.InnerException.ToString() : "..."));
Si on ne le fait pas, le compilateur comprend ceci :
Console.WriteLine(("Erreur : " + ex.Message + ex.InnerException) != null ? ex.InnerException.ToString() : "...");
Et évidemment, comme il n’y a pas d’InnerException, le code lève un NullReferenceException, qui n’est pas catchée dans notre cas, vu qu’on se trouve déjà dans une section de catch…
Du coup, on voit mieux ce qui parait contre-intuitif : l’opérateur de concaténation regroupe des chaînes, mais aussi une instance de System.Object, en l’occurrence ex.InnerException, sur laquelle il va automatiquement appliquer un ToString. Or, visuellement, on a plus tendance à associer fortement ex.InnerException avec le test de différence à null.
Personnellement, je ne vois pas pourquoi l’opérateur ternaire a une précédence aussi basse… Peut-être que ça a un lien avec le fait qu’elle remplace une structure if…else qui aurait alors englobé les paramètres, et aurait alors cédé automatiquement la priorité à l’évaluation complète des paramètres ?
Pour la liste complète des précédences des opérateurs C#, voir la documentation MSDN sur le sujet.