Aller au contenu principal

Qualité de code — Nullable, Analyseurs, StyleCop

Notions théoriques

Nullable Reference Types (C# 8+)

Avant C# 8, toute variable de type référence (string, classe...) pouvait implicitement valoir null, sans que le compilateur ne vous avertisse. Le résultat : des NullReferenceException en production.

Depuis C# 8, les Nullable Reference Types permettent de distinguer :

  • string : ne peut jamais être null (le compilateur le garantit)
  • string? : peut être null (vous devez le vérifier explicitement)

Pour activer cette fonctionnalité dans tout le projet :

<!-- MonProjet.csproj -->
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>

Ou fichier par fichier :

#nullable enable

public class Personnage
{
public string Nom { get; set; } = ""; // jamais null
public string? Description { get; set; } // peut être null
}

Opérateurs liés aux nulls

OpérateurNomExemple
?.Déréférencement conditionnelpersonnage?.Nom (retourne null si personnage est null)
??Coalescence nullepersonnage?.Nom ?? "Inconnu" (valeur par défaut si null)
??=Assignation si nulldescription ??= "Aucune description";
!Null-forgivingstring nom = personnage!.Nom; (supprime le warning)
attention

L'opérateur ! (null-forgiving) doit être utilisé avec parcimonie : il dit au compilateur "je garantis que ce n'est pas null", mais si vous avez tort, vous aurez quand même une NullReferenceException à l'exécution.

Roslyn Analyzers

Les Roslyn Analyzers sont des outils d'analyse statique intégrés à la chaîne de compilation .NET. En .NET 8, un ensemble d'analyseurs est activé par défaut et signale des problèmes de :

  • Style : nommage des variables, propriétés, méthodes
  • Performance : string concaténation dans une boucle, StringBuilder recommandé
  • Fiabilité : await oublié, using manquant sur les IDisposable

Ces warnings apparaissent dans Visual Studio et dans la sortie de dotnet build.

StyleCop

StyleCop.Analyzers applique un ensemble de règles de style de code (nommage, espaces, documentation XML). Il se configure via un fichier .editorconfig.

Installation :

dotnet add package StyleCop.Analyzers

Exemple de règles StyleCop courantes :

  • Toutes les méthodes publiques doivent avoir un commentaire XML (///)
  • Les using doivent être à l'intérieur du namespace
  • Les accolades ouvrantes doivent être sur une nouvelle ligne

SonarAnalyzer.CSharp

SonarAnalyzer.CSharp détecte les bugs potentiels, les vulnérabilités de sécurité et les "code smells" (mauvaises pratiques). Il est distribué via NuGet.

dotnet add package SonarAnalyzer.CSharp

dotnet format

La commande dotnet format applique automatiquement les règles de formatage définies dans .editorconfig :

dotnet format # Formater tout le projet
dotnet format --verify-no-changes # Vérifier sans modifier (pour la CI)

.editorconfig

Le fichier .editorconfig à la racine du projet définit les règles de style partagées par toute l'équipe :

[*.cs]
# Indentation
indent_style = space
indent_size = 4

# Nommage
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private

# Nullable
dotnet_diagnostic.CS8600.severity = error # Conversion possible de null
dotnet_diagnostic.CS8602.severity = error # Déréférencement possible d'une référence null

Exemple pratique

#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;

public class Personnage
{
public int Id { get; set; }
public string Nom { get; set; } = ""; // jamais null
public string? Description { get; set; } // peut être null
public int PointsDeVie { get; private set; } = 100;

// Méthode qui retourne null si le personnage est mort
public string? ObtenirStatut()
{
if (PointsDeVie <= 0)
return null; // null explicitement autorisé par string?

return $"{Nom} est vivant ({PointsDeVie} PDV)";
}
}

class Program
{
static void Main()
{
var personnages = new List<Personnage>
{
new Personnage { Id = 1, Nom = "Aragorn", Description = "Roi du Gondor" },
new Personnage { Id = 2, Nom = "Gandalf" }, // Description = null
};

foreach (var p in personnages)
{
// ?. : évite NullReferenceException si Description est null
int longueur = p.Description?.Length ?? 0;
Console.WriteLine($"{p.Nom} — Description : {longueur} caractères");

// ?? : valeur par défaut si null
string statut = p.ObtenirStatut() ?? "Décédé";
Console.WriteLine($" Statut : {statut}");
}

// ??= : initialisation paresseuse
var premierPersonnage = personnages.FirstOrDefault();
premierPersonnage!.Description ??= "Aucune description disponible";
Console.WriteLine($"\n{premierPersonnage.Nom} : {premierPersonnage.Description}");
}
}

Test de mémorisation/compréhension


Que signifie 'string?' en C# avec Nullable Reference Types activé ?


Quel opérateur retourne une valeur par défaut si l'expression à gauche est null ?


Quelle commande applique automatiquement les règles de formatage définies dans .editorconfig ?


Quel est le rôle du fichier .editorconfig ?


À quoi sert l'opérateur '!' (null-forgiving) en C# ?


TP pour réfléchir et résoudre des problèmes

Dans ce TP, vous allez activer les Nullable Reference Types sur le projet RPG et corriger les avertissements du compilateur.

Étape 1 — Activer Nullable dans le fichier de projet

Activez la fonctionnalité Nullable Reference Types en modifiant le fichier .csproj.


Bonne pratique - Activer Nullable dès le début du projet

Activer <Nullable>enable</Nullable> sur un projet existant fait souvent apparaître des dizaines de warnings. Il est beaucoup plus simple de l'activer dès la création du projet et de traiter les cas un par un au fur et à mesure. Sur un projet existant, activez-le progressivement fichier par fichier avec #nullable enable.

Étape 2 — Corriger les propriétés non nullables

Après activation, certaines propriétés string sans valeur par défaut génèrent un warning. Corrigez la classe Personnage.


Bonne pratique - Distinguer les propriétés nullables des non-nullables

Chaque propriété doit signaler clairement son comportement : string Nom = l'appelant peut compter sur une valeur non-null ; string? Description = l'appelant doit vérifier avant d'utiliser. Cette distinction rend les contrats de vos classes explicites et réduit les bugs.

Étape 3 — Utiliser l'opérateur ?? pour une valeur par défaut

Dans une méthode d'affichage, utilisez ?? pour gérer le cas où Description est null.


Bonne pratique - Toujours gérer le cas null des propriétés nullable

Dès qu'une propriété est déclarée string?, son utilisation doit être encadrée par ?., ?? ou un test explicite if (Description != null). Ne supprimez pas le warning avec ! sauf si vous êtes certain à 100% que la valeur n'est pas null à cet endroit du code.

📌 Une solution