.NET 11 Preview 3 : Une revue de développeur
.NET 11 arrive à son troisième aperçu environ six mois avant la GA prévue en novembre 2026, et contrairement à Preview 1 et 2, qui étaient principalement de la plomberie, Preview 3 est celui où vous pouvez réellement ressentir la forme de la sortie.
Runtime Async cesse d'être une expérimentation de "preview-feature gate", le JIT obtient un autre lot d'optimisations "argent gratuit", ASP.NET Core obtient Zstandard par défaut, et les types d'union de C# 15 - la fonctionnalité de langage que les gens demandent depuis presque une décennie.
Ce n'est pas une version LTS, .NET 11 est la prochaine version de Support à Terme Standard, avec 24 mois de support, ce qui change déjà le calcul pour savoir si vous mettez à niveau. Ce qui suit est ce qui mérite réellement votre attention en tant que développeur, et où sont encore les angles rugueux.
C# 15 types d'union : le grand soir
Si vous avez écrit des bibliothèques OneOf<T1, T2>, créé des hiérarchies d'enregistrement scellées à la main, ou simplement envié les développeurs F#, voici le gros titre. C# 15 introduit un mot-clé union qui déclare qu'une valeur est exactement un des types fixes, avec une exhaustivité imposée par le compilateur. Les types d'union sont arrivés dans Preview 2; Preview 3 perfectionne le support IDE autour d'eux.
L'approche de l'équipe C# est plus proche d'une union de types que des unions discriminantes F# : les types membres sont des types existants que vous avez définis séparément, pas des cas de balises nichés à l'intérieur de la déclaration d'union. Une union est essentiellement une structure qui enveloppe un objet et limite ce qui peut y entrer. Depuis le site d'appel, cela semble natif - conversion implicite des types de membres dans l'union, expressions switch exhaustives sur Pet.Value, et aucun bras par défaut n'est nécessaire lorsque le compilateur peut voir tous les cas.
[Union]
public partial struct Pet : IUnion
{
public Pet(Dog dog);
public Pet(Cat cat);
public Pet(Bird bird);
}
string Describe(Pet pet) => pet.Value switch
{
Dog d => $"Dog named {d.Name}",
Cat c => $"Cat ({c.Color})",
Bird b => $"Bird, {b.Species}",
// no default - compiler knows the set is closed
};
[Union]
public partial struct Pet : IUnion
{
public Pet(Dog dog);
public Pet(Cat cat);
public Pet(Bird bird);
}
string Describe(Pet pet) => pet.Value switch
{
Dog d => $"Dog named {d.Name}",
Cat c => $"Cat ({c.Color})",
Bird b => $"Bird, {b.Species}",
// no default - compiler knows the set is closed
};
<Union>
Public Partial Structure Pet
Implements IUnion
Public Sub New(dog As Dog)
End Sub
Public Sub New(cat As Cat)
End Sub
Public Sub New(bird As Bird)
End Sub
End Structure
Function Describe(pet As Pet) As String
Return pet.Value Select Case
Case d As Dog
Return $"Dog named {d.Name}"
Case c As Cat
Return $"Cat ({c.Color})"
Case b As Bird
Return $"Bird, {b.Species}"
' no default - compiler knows the set is closed
End Select
End Function
Les avantages sont réels : types fermés, pas d'états invalides, exhaustivité au moment de la compilation au lieu de NotImplementedExceptionat à 3 heures du matin. Ajoutez un Requin à l'union et chaque commutateur sur Pet s'illumine avec des avertissements jusqu'à ce que vous le manipuliez.
Les inconvénients sont également réels, et méritent d'être signalés dans toute critique honnête. La propriété Valeur exposée de l'objet est un odorat - il y a une discussion ouverte sur GitHub pour la masquer derrière quelque chose de plus sûr en termes de type. Les constructeurs publics définissent implicitement quels types l'union accepte, ce qui est ni découvrable ni explicite. L'interopérabilité F# n'est pas résolue (les deux modèles sont fondamentalement différents). Et l'histoire plus large de l'exhaustivité a encore des lacunes : les hiérarchies fermées et les enums fermés, les deux propositions qui compléteraient l'image, sont encore des propositions. Les unions seules sont excellentes. Les unions plus les enums fermés plus les hiérarchies fermées seraient un changement générationnel. Nous n'y sommes pas tout à fait.
Runtime Async V2 et améliorations du JIT
Runtime Async est la réécriture discrète de .NET sur la façon dont async/await s'exécute réellement. Au lieu que le compilateur C# émette une classe de machine d'état par méthode async, l'exécution elle-même gère la suspension et la reprise. Le résultat visible : des traces de pile plus propres, des allocations plus petites, et un débogueur qui ne vous oblige pas à faire défiler au-delà des cadres MoveNext pour trouver votre propre code.
Dans la Preview 3, l'Async Runtime abandonne l'exigence EnablePreviewFeatures. Vous activez toujours l'interrupteur de fonctionnalité - <Features>runtime-async=on</Features> - mais vous n'avez plus besoin de choisir chaque appel d'API dans le territoire de prévisualisation. Le support NativeAOT et ReadyToRun a également été lancé dans cet aperçu, ce qui comble l'écart entre les scénarios JIT et AOT. Les objets de continuation sont réutilisés plus agressivement, et les variables locales qui n'ont pas changé ne sont pas sauvegardées lors de la suspension. Dans les chemins de code intensifs en async - pensez à un pipeline Kestrel ou à un travailleur de requêtes EF Core - cela représente une diminution significative de la pression des allocations.
Le JIT a récolté son lot habituel de "votre code existant est maintenant plus rapide, ne faites rien":
- Les expressions de commutation à cibles multiples comme x is 0 ou 1 ou 2 ou 3 ou 4 se regroupent désormais en vérifications sans branche.
- Les vérifications des limites sur les motifs valeurs[^1] + valeurs[^2] sont éliminées de manière plus agressive, et le cas commun i + cns < len dans les boucles s'effondre proprement.
- Les chicanes d'int en virgule flottante et d'int non signé en double sont plus rapides sur les matériels x86 pré-AVX-512 - de niche, mais réel si vous êtes sur de vieilles machines.
Les utilisateurs de WebAssembly obtiennent un chargement de charge utile WebCIL directement dans le runtime, de meilleurs symboles de débogage, et float[] / Span<float> / ArraySegment<float> sans le surcoût du round-trip. Aucun de ces points n'est individuellement dramatique, mais ensemble ils constituent le genre de travail de composition qui fait que Blazor WASM semble moins être un compromis.
La contrepartie est le plancher matériel. .NET 11 élève les exigences minimales en matière d'ensemble d'instructions sur x86/x64 et Arm64. Apple Silicon et la plupart des SBC Linux sont bien - la cible ReadyToRun sur Arm64 ajoute juste LSE - mais le matériel x86 très ancien est exclu. Auditez votre flotte avant de supposer une mise à niveau sur place.
ASP.NET Core et Blazor
Zstandard est le gros titre ici. ASP.NET Core prend désormais en charge zstd à la fois pour la compression de la réponse et la décompression de la requête, et il est activé par défaut lorsque vous ajoutez le middleware. La configuration a la forme que vous attendiez :
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure<ZstandardCompressionProviderOptions>(options =>
{
options.CompressionOptions = new ZstandardCompressionOptions { Quality = 6 };
});
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure<ZstandardCompressionProviderOptions>(options =>
{
options.CompressionOptions = new ZstandardCompressionOptions { Quality = 6 };
});
Imports Microsoft.Extensions.DependencyInjection
builder.Services.AddResponseCompression()
builder.Services.AddRequestDecompression()
builder.Services.Configure(Of ZstandardCompressionProviderOptions)(Sub(options)
options.CompressionOptions = New ZstandardCompressionOptions With {.Quality = 6}
End Sub)
Pour les APIs servant des données JSON ou texte à des clients qui parlent déjà zstd - de plus en plus courantes dans les écosystèmes mobiles et adjacents à gRPC - c'est une victoire de bande passante mesurable sans avoir à dépendre d'une bibliothèque tierce. C'est aussi, notamment, une contribution communautaire plutôt qu'une interne à Microsoft, ce qui est un signal positif.
Blazor's Virtualize<TItem> arrête enfin de supposer que chaque ligne a la même hauteur. C'était une irritation de longue date : toute liste avec un contenu variable - commentaires, messages de chat, tout ce qui contient du texte à plusieurs lignes - nécessitait des solutions de contournement manuelles. Maintenant, le composant mesure les éléments à l'exécution. La version Preview 3 résout également une série de bogues Blazor: une référence null dans Virtualize, détection de conteneur de défilement avec overflow-x, le modèle Web Worker dans les applications WASM publiées, TempData cas particuliers de chargement paresseux, et une fuite IJSObjectReference dans ResourceCollectionProvider. Chacun pris isolément est petit, mais ensemble, ils représentent le genre de nettoyage qui indique que le cadre mûrit au-delà du stade "une nouvelle chose à chaque sortie".
Kestrel commence également à traiter les requêtes HTTP/3 sans attendre le flux de contrôle et le cadre SETTINGS, ce qui réduit la latence de la première requête sur les nouvelles connexions. Si vous avez mesuré les P99 HTTP/3 et remarqué des queues étranges de démarrage à froid, c'est votre correctif.
.NET MAUI
MAUI dans Preview 3 consiste principalement à combler les lacunes qui l'ont fait ressembler à une version bêta trop longtemps. Le contrôle Map obtient le clustering de pins, des icônes de pins personnalisées, un style JSON personnalisé, et des événements de clic sur des cercles, des polygones et des polylignes - tout ce dont les véritables interfaces utilisateur de cartes de production ont besoin et qui nécessitaient auparavant des gestionnaires personnalisés par plateforme. Un LongPressGestureRecognizer intégré est maintenant disponible sans que vous ayez besoin de le créer vous-même. Les déclarations de namespaces XAML implicites sont activées par défaut, ce qui réduit le modèle à la tête de chaque fichier.
La parité de la plateforme reçoit une amélioration : Permissions.PostNotifications est maintenant implémenté sur iOS (il était uniquement sur Android), et Android reçoit un support en aperçu pour Android 17 et le niveau d'API 37.
L'évaluation honnête : c'est une itération stable et sensée, pas une réinvention. MAUI en 2026 est dans un bien meilleur état que MAUI en 2023, mais si vous vous en êtes éloigné plus tôt dans sa vie, Preview 3 ne vous ramènera pas à lui seul. Si vous êtes déjà sur MAUI, ce sont précisément les changements de QoL que vous souhaitez.
SDK, CLI et veille .NET
C'est la section où les petites choses s'accumulent. Quelques-unes qui, je pense, changent vraiment le flux de travail quotidien :
.NET sln peut maintenant créer et éditer des filtres de solution (.slnf) directement depuis la CLI. Pour les monorepos et les solutions de style Microsoft, l'ouverture d'une SLN de 200 projets pour en travailler sur trois a été un véritable coût. Maintenant, vous pouvez les cibler depuis le terminal :
dotnet new slnf --name MyApp.slnf
dotnet sln MyApp.slnf add src/Lib/Lib.csproj
dotnet new slnf --name MyApp.slnf
dotnet sln MyApp.slnf add src/Lib/Lib.csproj
Les applications basées sur des fichiers (le workflow .NET run app.cs) prennent enfin en charge #:include, ce qui signifie que les scripts C# peuvent séparer des assistants dans des fichiers distincts. Combiné à l'achèvement de l'éditeur pour la directive dans Roslyn, cela pousse les applications basées sur des fichiers du statut "jouet" à "viable pour de véritables outils d'automatisation" - la niche que PowerShell et les petits scripts Python ont détenue pendant des années.
.NET run -e FOO=BAR vous permet de passer les variables d'environnement sur la ligne de commande sans exporter l'état du shell ou modifier les profils de lancement. Petit, mais si vous avez déjà eu trois terminaux ouverts avec différentes valeurs ASPNETCORE_ENVIRONMENT, vous connaissez la douleur.
.NET watch s'intègre avec les hôtes d'application Aspire, se relance automatiquement après un crash au prochain changement de fichier, et gère Ctrl+C plus gracieusement pour WinForms et WPF (une coupure de papier perpétuelle). .NET format accepte --framework pour les projets multi-ciblés. .NET test en mode MTP prend en charge --artifacts-path. Et .NET tool exec / dnx ne demande plus une approbation supplémentaire, ce qui était un point de friction pour les exécutions d'outils ponctuelles.
Les points douloureux
Une évaluation équilibrée doit traiter des angles rugueux, et Preview 3 en a.
L'histoire des outils est difficile. Visual Studio 2026 est encore en aperçu six mois après la livraison de .NET 10, et les agents de construction hébergés par Microsoft n'ont pas encore de support stable pour VS 2026. Un changement dans une version du patch du SDK .NET 10 a nécessité MSBuild 18 (VS 2026), ce qui est une violation flagrante des garanties semver que Microsoft promeut. Quiconque exécutant CI sur des agents hébergés par Microsoft a dû soit épingler SDK 10.0.4, soit passer à des images de construction en aperçu. Si vous envisagez de déplacer les pipelines CI vers les aperçus de .NET 11, attendez-vous à plus de la même chose - les SDKs en aperçu ont, de l'aveu même de l'équipe, cassé des éléments dans les versions 10.0.2 et 10.0.3 avant de se stabiliser.
L'exécution asynchrone est toujours en option. Même avec la barrière de fonctionnalités en avant-première disparue, vous devez activer runtime-async=on. C'est bien pour le code d'un champ vierge; pour les bibliothèques distribuées sur NuGet, vous ne pouvez toujours pas supposer que vos utilisateurs ont activé le commutateur, donc les avantages pratiques sont reportés jusqu'à ce que la fonctionnalité soit activée par défaut (pas dans .NET 11).
Augmentations des exigences matérielles. Les exigences minimales du jeu d'instructions x86/x64 ont été relevées. La plupart des équipes ne le remarqueront pas. Certaines le découvriront au moment du déploiement si elles n'auditent pas d'abord.
STS, pas LTS. .NET 11 est pris en charge pendant 24 mois. .NET 10, le LTS actuel, obtient 36 mois. Pour les entreprises avec un rythme de mise à niveau lent, .NET 10 reste le choix le plus prudent, et adopter .NET 11 signifie s'engager à une autre mise à niveau en 2028. Les arguments en faveur de l'adoption d'un STS sont les fonctionnalités; les arguments contre sont le calendrier.
Aperçu signifie aperçu. Ce n'est pas un coup de gueule sur la stabilité - le processus d'aperçu de Microsoft a été bon - mais l'Aperçu 3 n'est pas un candidat à la libération. Les déploiements en production attendent RC1 au plus tôt. Les outils internes, les projets secondaires et l'exploration sont le bon champ d'application pour le moment.
Verdict
Si vous écrivez du C# tous les jours, il vaut la peine d'installer et de jouer avec .NET 11 Preview 3 cette semaine - particulièrement pour mettre la main sur les types d'union, qui sont le changement de langage le plus significatif depuis des années. Si vous maintenez des bibliothèques, le travail sur le JIT et Async Runtime signifie que votre code va devenir plus rapide sur .NET 11 sans modifications, ce qui est le meilleur type de mise à niveau. Si vous expédiez des applications MAUI, le travail sur la carte et les gestes est un véritable progrès.
Si vous exécutez des charges de travail en production .NET, la réponse est l'ennuyeuse : continuez à planifier, continuez à observer, et ajoutez la GA en novembre à vos favoris. Les pièces qui excitent se concrétisent, mais la chaîne d'outils - VS, les agents de build et la cadence de correction du SDK - est là où se trouve la friction réelle, et elle n'a pas encore été résolue.
| --- |
Sources : Annonce du blog .NET, Quoi de neuf dans .NET 11 (Microsoft Learn), Notes de version Runtime, Notes de version ASP.NET Core, Notes de version SDK, Explorer les types d'union dans C# 15.