En C#, la programmation asynchrone repose principalement sur la classe Task (introduite avec .NET Framework 4) et les mots-clés async/await (C# 5). Mais depuis .NET Core 2.0, un nouveau type est apparu : ValueTask. À quoi sert-il ? Quand faut-il l’utiliser ? Est-ce vraiment une alternative à Task ?
Task
Task représente une opération asynchrone en cours ou à venir.
public Task<int> GetValueAsync()
{
return Task.FromResult(42);
}
Dans le code ci-dessus, un Task est créé même si le résultat est immédiatement disponible. Il y a une allocation mémoire inutile à chaque utilisation de Task.
Dans la majorité des cas, ce coût est négligeable. Mais dans les scénarios où l’on veut optimiser l’utilisation des ressources, cela peut devenir un problème.
ValueTask
ValueTask est une structure introduite en C# 7. L’intérêt de ValueTask apparait lorsque le résultat de notre exécution est souvent immédiatement disponible mais pas toujours. Il nous évite une allocation mémoire inutile dans ce cas.
Exemple typique avec un cache
ValueTask<int> GetValueAsync()
{
if (_cache.TryGetValue("key", out var value))
return new ValueTask<int>(value); // pas d'allocation
return new ValueTask<int>(GetValueFromDbAsync()); // exécution asynchrone
}
async Task<int> GetValueFromDbAsync()
{
await Task.Delay(1000); // simulation I/O
return 42;
}
Dans cet exemple, on fait souvent appel à la valeur contenue dans le cache, donc pas d’allocation mémoire. Mais quelques fois, on va récupérer la valeur dans la base de données. Pour ce dernier scénario, on utilise un Task qui entraine une coûteuse allocation mémoire.
Conclusion
ValueTask est un outil d’optimisation, pas un remplaçant de Task. Dans la grande majorité des cas, Task reste le meilleur choix. Il est plus simple à implémenter. Réservez ValueTask aux scénarios critiques où chaque allocation compte.



