Les nouveautés de C# 6 (3) : l’opérateur de nullité conditionnelle

Dans ce troisième article sur les nouveautés de C# 6, voici une nouveauté qui me tient personnellement à coeur. La demande UserVoice avait connu son petit succès, et nous y voilà : l’opérateur “?”. Tous les développeurs C# sont passés par là : tester la nullité d’un membre avant d’effectuer une opération dessus. Et tout le monde s’est un jour heurté à la bonne vieille NullReferenceException, dans le cas où elle n’avait pas été prévue, donnant le plus souvent lieu à un fix de type
if (monObjet != null) { //faire des trucs }
Hallelujah, mes frères (et mes quelques rares soeurs), ce temps est désormais (plus ou moins) révolu grâce à ce nouvel opérateur. Plutôt que d’écrire :
int? length = null; if (customers != null) { length = customers.Length; }
Vous pourrez désormais écrire :
int? length = customers?.Length; // null si customers est null
Attention, le membre auquel on affecte une valeur doit naturellement être nullable. L’avantage, c’est que le développeur devra se poser la question au moment où il écrit le code. Voilà pour l’utilisation basique de l’opérateur. Mais nous allons voir qu’il est non seulement extrêmement simple d’utilisation mais aussi puissant. Nous allons donc pouvoir écrire des choses comme :
Customer first = customers?[0]; // null si customers est null
L’opérateur ? sera également un excellent complément de l’opérateur de nullité coalescent ?? :
int length = customers?.Length ?? 0; // 0 si customers est null
Cet opérateur se comporte comme un court-circuit : tous les membres à droite de l’opérateur ne seront évalués que si les membres de gauche sont non-nuls.
int? first = customers?[0].Orders.Count();
est rigoureusement l’équivalent de
int? first = (customers != null) ? customers[0].Orders.Count() : null;
sauf que customers n’est évalué qu’une fois. On peut également chaîner cet opérateur :
int? first = customers?[0].Orders?.Count();
Dans ce cas, first sera non-nul si et seulement si le client existe et qu’il a des commandes. De quoi nous économiser bien des contrôles verbeux. Attention à bien commenter votre code, en revanche. L’opérateur est bien plus discret qu’un gros if ! Nous venons de voir qu’il est possible de coller un crochet ouvrant directement après ce nouvel opérateur. En revanche, c’est impossible d’appeler directement un delegate après avoir testé sa présence. Pour Mads Torgersen, la syntaxe serait trop ambigüe. La solution de contournement consistera à passer par un appel de la méthode Invoke :
if (predicate?.Invoke(e) ?? false) { … }
Cela ouvre la possibilité d’une utilisation assez répandue de ce pattern pour déclencher des événements. Par exemple, pour ce bon vieux PropertyChanged :
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(X)));
Nous verrons ce nouvel opérateur nameof dans un prochain article. Cette syntaxe permet de déclencher un événement, si et seulement si, un membre est non-nul et le tout est thread safe, car le membre de gauche n’est évalué qu’une fois et est affecté à une variable temporaire. En voilà une chouette nouveauté ! L’opérateur est simple à utiliser et suffisamment puissant. Il faudra, en revanche, veiller à l’utiliser à bon escient et se poser les bonnes questions : est-il normal que mon objet soit null ? Que dois-je faire s’il l’est ? Si vous intervenez un jour sur du code plein de “?.”, c’est qu’il y a sans doute un problème sous-jacent. Pour le reste, ça devrait nous économiser pas mal de lignes de code !