C#.NETLanguage FeaturesReadabilityClean Code

Discard _ en C# : out, déconstruction, pattern matching, lambdas, résultat ignoré, et le piège du fire-and-forget avec _ = Task qui masque les exceptions.

Yva Hajatiana
11 mars 2026
5 min de lecture
Partager :X / TwitterLinkedIn
Composition éditoriale sur la précision et la fiabilité logicielles

Discard _ en C# : usages utiles, pièges et lisibilité

Le discard _ en C# paraît simple : il sert à ignorer une valeur dont on n'a pas besoin. Pourtant, comme beaucoup de petites fonctionnalités de langage, sa vraie valeur dépend du contexte.

Bien utilisé, il rend l'intention plus explicite. Mal utilisé, il cache une API mal pensée, ou pire, une exception qui ne sera jamais observée.

Ce que signifie vraiment _

Un discard indique une chose précise : cette valeur existe, mais je choisis explicitement de ne pas l'utiliser. Le compilateur n'alloue pas de variable lisible : _ ne peut pas être relu ensuite.

if (int.TryParse(input, out _))
{
    // Only the format is validated, the parsed value is not kept
}

Ici, _ rend l'intention très claire : on ne veut pas exploiter l'entier analysé, on veut seulement savoir si la conversion réussit.

Les cas d'usage utiles

1. Paramètres out sans conservation de la valeur

bool isValid = Guid.TryParse(id, out _);

2. Déconstruction partielle

var (_, status, lastUpdated) = GetOrderSummary(orderId);

Quand certaines informations ne servent pas, le discard évite d'introduire des variables inutiles que l'analyse de code signalerait.

3. Pattern matching

if (message is OrderCreated(_, var customerId, _))
{
    // Only customerId matters here
}

Dans une expression switch, _ joue aussi le rôle du cas par défaut :

string label = status switch
{
    OrderStatus.Paid => "Payée",
    OrderStatus.Shipped => "Expédiée",
    _ => "En cours",
};

4. Paramètres de lambda ignorés

Depuis C# 9, plusieurs paramètres d'une lambda peuvent être ignorés, ce qui est courant avec les gestionnaires d'événements :

button.Click += (_, _) => RefreshView();

app.MapGet("/health", (HttpContext _) => Results.Ok());

5. Ignorer volontairement un résultat de retour

Assigner à _ documente qu'une valeur de retour est délibérément ignorée et fait taire l'avertissement des analyseurs :

_ = dictionary.Remove(key); // the bool result is not needed

Le piège : _ = TaskAsync()

Le cas le plus dangereux est la tâche ignorée :

_ = SendWelcomeEmailAsync(user); // fire-and-forget

Ce code compile sans avertissement, mais :

  • une exception levée par SendWelcomeEmailAsync n'est jamais observée ni journalisée ;
  • la requête HTTP peut se terminer, et ses services scoped (DbContext, par exemple) être libérés, pendant que la tâche s'exécute encore ;
  • rien ne garantit que le travail se termine si l'application s'arrête.

Dans une application ASP.NET Core, préférez une file en mémoire (Channel<T>) consommée par un BackgroundService, ou une file de messages durable si le travail ne doit pas être perdu. Si un fire-and-forget est vraiment acceptable, encapsulez-le dans une méthode qui capture et journalise l'exception :

static async Task RunAndLogAsync(Func<Task> work, ILogger logger)
{
    try
    {
        await work();
    }
    catch (Exception exception)
    {
        logger.LogError(exception, "Background work failed.");
    }
}

_ = RunAndLogAsync(() => SendWelcomeEmailAsync(user), logger);

Le discard reste là, mais l'échec n'est plus silencieux. La gestion des tâches concurrentes est approfondie dans Task.WhenAny ou WhenEach : bien choisir, et la publication fiable de messages après une écriture dans Inbox et Outbox en .NET.

Pourquoi c'est utile

Le principal gain n'est pas de gagner quelques caractères, mais de supprimer le bruit. Au lieu d'écrire :

Guid ignored;
bool ok = Guid.TryParse(id, out ignored);

on écrit directement l'intention :

bool ok = Guid.TryParse(id, out _);

Moins de variables parasites, moins de confusion, moins de tentation de réutiliser une valeur sans vraie raison.

Un détail de compatibilité : _ peut encore être un nom de variable

Pour préserver le code ancien, _ n'est un discard que s'il n'existe pas déjà une variable nommée _ dans la portée. Ce code est donc trompeur :

var _ = ComputeTotal();   // declares a real variable named _
int.TryParse(text, out _); // now overwrites that variable

N'utilisez jamais _ comme nom de variable : les analyseurs .NET le signalent d'ailleurs. Dans une lambda à un seul paramètre, _ reste aussi un vrai paramètre, utilisable dans le corps.

Quand le discard devient un mauvais signal

Le discard ne doit pas servir à masquer un problème de conception. Si vous ignorez souvent la moitié des valeurs retournées par une méthode, le contrat mérite d'être revu.

var (_, _, _, discountCode, _, _, _) = pricingResult;

Ce code compile, mais il signale surtout que la structure retournée n'est pas adaptée au besoin du consommateur. Dans ce cas :

  • revoyez le type de retour, par exemple un record aux propriétés nommées ;
  • introduisez un contrat plus ciblé pour cet appelant ;
  • simplifiez l'API appelée.

Dans une déconstruction longue, _ répété plusieurs fois oblige le lecteur à reconstruire mentalement des positions anonymes. Un accès par propriété (pricingResult.DiscountCode) reste alors plus lisible.

Conclusion

Le discard _ en C# est une petite fonctionnalité, mais une bonne petite fonctionnalité : il rend explicite ce qu'on choisit de ne pas utiliser. Son meilleur usage consiste à clarifier l'intention. Son mauvais usage consiste à cacher un contrat mal pensé ou une tâche dont l'échec ne sera jamais vu.

La question n'est pas « peut-on l'utiliser ? ». La vraie question est : « est-ce que cela rend le code plus lisible, sans rien cacher d'important ? » D'autres fonctionnalités du langage sont présentées dans le parcours C# et ASP.NET Core, notamment switch et pattern matching en C#.

Mots-clés :C#.NETLanguage FeaturesReadabilityClean Code
Y

Yva Hajatiana

Lead développeur .NET à Paris et architecte logiciel, 10 ans d'expérience sur des SI critiques bancaires (.NET, Angular, systèmes distribués, IAM, DevOps).