Top 10 des nouveautés de C# 13/14 et .NET 9/10

Top 10 des nouveautés de C# 13/14 et .NET 9/10

Top 10 des nouveautés de C# 13/14 et .NET 9/10

Si vous êtes encore sur .NET 8, même si c’est une version LTS, elle ne sera plus supportée après novembre 2026. Depuis novembre 2024, deux versions de .NET (.NET 9 et .NET 10) et deux versions de C# sont sorties. Voici les fonctionnalités intéressantes sorties depuis novembre 2024 qui pourraient faire partie de votre mise à jour vers .NET 10.

Table des matières


1. Le mot-clé field

C# 13 (preview) / C# 14

Le mot-clé field permet d’éviter de déclarer un champ privé pour une propriété avec de la logique dans le setter.

// Avant
private string _nom;
public string Nom
{
    get => _nom;
    set => _nom = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}

// Après
public string Nom
{
    get;
    set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}

2. L’assignation null-conditionnelle

C# 14

L’opérateur ?. fonctionne maintenant du côté gauche d’une assignation, pas seulement pour la lecture.

// Avant
if (commande != null)
    commande.Client = client;

// Après
commande?.Client = client;

3. params collections

C# 13

params accepte maintenant n’importe quelle collection, y compris ReadOnlySpan<T>, ce qui évite l’allocation d’un tableau.

// Avant : allocation garantie
void Afficher(params string[] items) { ... }

// Après : zéro allocation
void Afficher(params ReadOnlySpan<string> items)
{
    foreach (var item in items)
        Console.WriteLine(item);
}

Afficher("un", "deux", "trois");

4. System.Threading.Lock

C# 13 / .NET 9

Il y a maintenant un type dédié pour les verrous au lieu d’utiliser un object.

// Avant
private readonly object _verrou = new();

lock (_verrou)
{
    // section critique
}

// Après
private readonly Lock _verrou = new();

using (_verrou.EnterScope())
{
    // section critique
}

EnterScope avec using libère le verrou même si une exception est lancée.


5. Task.WhenEach

.NET 9

Task.WhenEach permet d’itérer sur les tâches au fur et à mesure qu’elles se complètent, sans avoir à gérer la liste manuellement.

// Avant
var tâches = new List<Task<string>>(urls.Select(TéléchargerAsync));
while (tâches.Any())
{
    var terminée = await Task.WhenAny(tâches);
    tâches.Remove(terminée);
    Console.WriteLine(await terminée);
}

// Après
await foreach (var tâche in Task.WhenEach(urls.Select(TéléchargerAsync)))
{
    Console.WriteLine(await tâche);
}

6. Extension members

C# 14

Les membres d’extension permettent maintenant d’ajouter des propriétés et des membres statiques en plus des méthodes, dans un bloc extension.

public static class StringExtensions
{
    extension(string s)
    {
        public int NombreDeMots =>
            s.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;

        public static string Vide => string.Empty;
    }
}

var compte = "Bonjour tout le monde".NombreDeMots; // 3
var vide = string.Vide;

7. dotnet run app.cs

.NET 10

Il est maintenant possible d’exécuter un fichier C# directement sans créer de projet, avec des directives pour les dépendances NuGet.

#!/usr/bin/env dotnet
#:package Humanizer@2.14.1

using Humanizer;

var nombre = 42;
Console.WriteLine(nombre.ToWords()); // quarante-deux
dotnet run app.cs

Quand le script grossit, dotnet project convert le transforme en projet complet.


8. Validation intégrée dans les Minimal APIs

.NET 10

La validation des paramètres est maintenant intégrée dans les Minimal APIs, sans filtre personnalisé ni librairie tierce.

builder.Services.AddValidation();

app.MapPost("/produits", (Produit produit) => Results.Ok(produit));

public record Produit(
    [Required] string Nom,
    [Range(0.01, 10000)] decimal Prix
);

Si Nom est vide ou Prix hors limite, la réponse est automatiquement un ProblemDetails avec un statut 400.


9. Named query filters dans EF Core

EF Core 10

Les query filters ont maintenant un nom, ce qui permet de les désactiver individuellement avec IgnoreQueryFilters plutôt que tous en même temps.

modelBuilder.Entity<Article>()
    .HasQueryFilter("actif", a => a.EstActif)
    .HasQueryFilter("locataire", a => a.LocataireId == _locataireId);

var articles = await ctx.Articles
    .IgnoreQueryFilters("locataire")
    .ToListAsync();

10. HybridCache

ASP.NET Core 9

HybridCache combine IMemoryCache et IDistributedCache avec une seule API. Il gère aussi le cache stampede : si plusieurs requêtes arrivent en même temps pour une clé pas encore en cache, une seule ira chercher la donnée.

builder.Services.AddHybridCache();

var produit = await _cache.GetOrCreateAsync(
    $"produit-{id}",
    async cancel => await _repo.GetAsync(id, cancel)
);

Bonne migration vers .NET 10.


Cet article a été rédigé avec l’aide de l’IA et révisé par moi.


Suggestions de lecture :