1. Introduction
Imagine que ton code, c’est une pièce de théâtre, et les variables, ce sont les acteurs. À chaque fois qu’une variable arrive dans une nouvelle scène (un bloc de code), elle peut être “vivante” (elle a une valeur) ou “fantôme” (null). Mais voilà le souci : le spectateur — le compilateur — regarde la pièce non pas en live, mais juste sur le script. Il voit où les acteurs reçoivent leurs rôles (on leur assigne des valeurs), et où ils disparaissent (deviennent null). Mais il ne peut pas voir la réalité (l’exécution du programme) — il ne fait que deviner à partir du texte du script !
L’analyse statique du flux de données, c’est le process où le compilateur vérifie ton code avant même que le programme ne tourne, pour comprendre si quelqu’un ne va pas tenter de jouer la scène du “crash NullReferenceException”.
Le principe
Depuis C# 8.0 et l’apparition du mode Nullable Reference Types (NRT), le compilateur analyse si une variable peut devenir null dans une branche d’exécution. Et si oui — il te le dit direct. Il ne te laissera pas “utiliser le nom d’un acteur qui est soudainement devenu fantôme”.
2. Comment lire les avertissements du compilateur
Les grandes catégories d’avertissements
Quand le mode NRT est activé, le compilateur surveille de très près les variables de type référence (string, User, tableaux, objets). Voilà les types d’avertissements les plus courants :
- Possible null assignment — tu essaies d’assigner une valeur qui peut être null à une variable qui ne doit pas être null.
- Dereference of a possibly null reference — tu accèdes à un membre d’un objet, mais le compilateur doute qu’il ne soit pas null.
- Possible null return — une méthode peut retourner null alors que, d’après la signature, elle doit renvoyer une valeur.
- Nullable object must have a value — tu utilises .Value sur un type nullable sans vérifier.
- Unassigned non-nullable property — tu n’as pas assigné de valeur à un champ/propriété qui ne doit pas être null.
En général, dans l’IDE (genre Rider ou Visual Studio), ces avertissements sont soulignés en vaguelettes, et si tu passes la souris dessus, t’as une explication.
Exemple d’avertissement typique
#nullable enable
string? possibleNull = GetString();
// Oups, le compilateur râle : "Dereference of a possibly null reference"
int length = possibleNull.Length; // Avertissement !
Le compilateur n’est pas sûr que possibleNull ne soit pas null, donc accéder à .Length peut causer une exception.
Si la méthode ressemble à ça :
string? GetString() { ... }
Et tu écris :
string nonNullable = GetString();
Tu vas avoir un avertissement : possible passage non safe de null dans une variable non-nullable.
3. Le compilateur analyse ton code
Exemple 1 : assignation directe
string? name = null;
Console.WriteLine(name.Length); // Avertissement : possible accès à null
Là c’est clair : name vaut null, donc tenter d’accéder à une propriété — c’est potentiellement dangereux.
Exemple 2 : vérif sur null
string? name = GetUserName();
if (name != null)
{
Console.WriteLine(name.Length); // Tout roule !
}
else
{
Console.WriteLine("Nom non renseigné !");
}
Le compilateur pige que dans le bloc if (name != null) la variable est considérée comme safe. C’est ça, le flow analysis — l’analyse du flux des valeurs selon les conditions.
4. Le compilateur ne peut pas voir “à l’intérieur d’une méthode”
Il regarde juste la signature — si c’est marqué que la méthode retourne string?, il croit que ça peut être null, même si à l’intérieur ça retourne toujours une string.
Exemple 3 : retour de valeur depuis une méthode
string? GetMaybeName(bool useName)
{
if (useName)
return "Code Jedi";
else
return null;
}
void PrintLength()
{
string? name = GetMaybeName(false);
Console.WriteLine(name.Length); // Avertissement !
}
Le compilateur voit que GetMaybeName peut retourner null, donc il te demande de faire gaffe.
5. Scénarios fréquents et avertissements
Assigner un nullable à une variable non-nullable
string? maybeUser = GetUser();
string alwaysUser = maybeUser; // warning: possible null assignment
Pour virer l’avertissement :
string alwaysUser = maybeUser ?? "Invité";
Là, t’auras toujours une string — soit celle de la méthode, soit "Invité".
T’as oublié de checker .HasValue sur un value-type nullable
int? age = GetAge();
int realAge = age.Value; // warning: possible accès à null
Faut faire comme ça :
if (age.HasValue)
Console.WriteLine(age.Value);
Ou alors :
int realAge = age ?? -1;
Propriétés automatiques sans initialisation
class User
{
public string Name { get; set; } // warning: pas initialisé !
}
Solutions :
public string Name { get; set; } = "Sans nom";
ou
public string? Name { get; set; }
6. Cas compliqués d’analyse de flux
Analyse dans les boucles
string? text = null;
while (text == null)
{
text = Console.ReadLine();
}
Console.WriteLine(text.Length); // tout roule : le compilateur a pigé !
Différents chemins d’assignation
string? name;
if (Random.Shared.Next() % 2 == 0)
name = "Alice";
else
name = null;
Console.WriteLine(name.Length); // warning: possible null dereference
Le compilateur analyse tous les chemins. S’il y en a un où c’est null — il prévient.
7. Comment “rassurer” le compilateur (et quand il ne faut pas le faire)
Parfois, t’es sûr que la valeur ne peut pas être null, mais le compilateur n’y croit pas :
string? value = GetSomething();
if (value == null)
throw new Exception("Valeur attendue !");
Console.WriteLine(value.Length); // L’avertissement disparaît
Ou tu utilises l’opérateur ! :
string? value = GetSomething();
Console.WriteLine(value!.Length); // Plus d’avertissement, mais potentiellement risqué
Ça s’appelle le null-forgiving operator. Il ne vérifie rien — il dit juste au compilateur : “t’inquiète, tout est sous contrôle”. Mais si tu te plantes — tu te prends une exception à l’exécution.
GO TO FULL VERSION