Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article décrit les nouvelles fonctionnalités des bibliothèques .NET pour .NET 10.
Cryptographie
- Rechercher des certificats par empreintes autres que SHA-1
- Rechercher des données encodées PEM dans ASCII/UTF-8
- Algorithme de chiffrement pour l’exportation PKCS#12/PFX
- Chiffrement post-quantique (PQC)
Rechercher des certificats par empreintes autres que SHA-1
La recherche de certificats de manière unique par empreinte numérique est une opération assez courante, mais la X509Certificate2Collection.Find(X509FindType, Object, Boolean) méthode (pour le FindByThumbprint mode) recherche uniquement la valeur de l’empreinte SHA-1.
Il existe un certain risque d’utiliser la Find méthode permettant de trouver SHA-2-256 (« SHA256 ») et les empreintes numériques SHA-3-256, car ces algorithmes de hachage ont les mêmes longueurs.
Au lieu de cela, .NET 10 introduit une nouvelle méthode qui accepte le nom de l’algorithme de hachage à utiliser pour la correspondance.
X509Certificate2Collection coll = store.Certificates.FindByThumbprint(HashAlgorithmName.SHA256, thumbprint);
Debug.Assert(coll.Count < 2, "Collection has too many matches, has SHA-2 been broken?");
return coll.SingleOrDefault();
Rechercher des données encodées PEM dans ASCII/UTF-8
L’encodage PEM (Courrier Amélioré de Confidentialité à l’origine, mais désormais utilisé largement en dehors de l’e-mail) est défini pour « texte », ce qui signifie que cette PemEncoding classe a été conçue pour s’exécuter sur String et ReadOnlySpan<char>. Toutefois, il est courant (en particulier sur Linux) d’avoir quelque chose comme un certificat écrit dans un fichier qui utilise l’encodage ASCII (chaîne). Historiquement, cela signifiait que vous deviez ouvrir le fichier et convertir les octets en caractères (ou une chaîne) avant de pouvoir utiliser PemEncoding.
La nouvelle PemEncoding.FindUtf8(ReadOnlySpan<Byte>) méthode tire parti du fait que PEM est défini uniquement pour les caractères ASCII 7 bits, et que l'ASCII 7 bits a une concordance parfaite avec les valeurs UTF-8 à un octet. En appelant cette nouvelle méthode, vous pouvez ignorer la conversion UTF-8/ASCII-to-char et lire le fichier directement.
byte[] fileContents = File.ReadAllBytes(path);
-char[] text = Encoding.ASCII.GetString(fileContents);
-PemFields pemFields = PemEncoding.Find(text);
+PemFields pemFields = PemEncoding.FindUtf8(fileContents);
-byte[] contents = Base64.DecodeFromChars(text.AsSpan()[pemFields.Base64Data]);
+byte[] contents = Base64.DecodeFromUtf8(fileContents.AsSpan()[pemFields.Base64Data]);
Algorithme de chiffrement pour l’exportation PKCS#12/PFX
Les nouvelles ExportPkcs12 méthodes sur X509Certificate permettent aux appelants de choisir les algorithmes de chiffrement et de synthèse à utiliser pour générer le résultat :
- Pkcs12ExportPbeParameters.Pkcs12TripleDesSha1 indique la norme de facto de l'époque de Windows XP. Il produit une sortie prise en charge par presque chaque bibliothèque et plateforme qui prend en charge la lecture de PKCS#12/PFX en choisissant un algorithme de chiffrement plus ancien.
- Pkcs12ExportPbeParameters.Pbes2Aes256Sha256 indique qu’AES doit être utilisé au lieu de 3DES (et SHA-2-256 au lieu de SHA-1), mais que la sortie peut ne pas être comprise par tous les lecteurs (comme Windows XP).
Si vous souhaitez un contrôle encore plus grand, vous pouvez utiliser la surcharge qui accepte un PbeParameters.
Chiffrement post-quantique (PQC)
.NET 10 inclut la prise en charge de trois nouveaux algorithmes asymétriques : ML-KEM (FIPS 203), ML-DSA (FIPS 204) et SLH-DSA (FIPS 205). Les nouveaux types sont les suivants :
- System.Security.Cryptography.MLKem
- System.Security.Cryptography.MLDsa
- System.Security.Cryptography.SlhDsa
Parce qu’il ajoute peu d’avantages, ces nouveaux types ne dérivent pas de AsymmetricAlgorithm. Plutôt que l’approche AsymmetricAlgorithm de création d’un objet, puis l’importation d’une clé dans celle-ci, ou la génération d’une nouvelle clé, les nouveaux types utilisent toutes des méthodes statiques pour générer ou importer une clé :
using System;
using System.IO;
using System.Security.Cryptography;
private static bool ValidateMLDsaSignature(ReadOnlySpan<byte> data, ReadOnlySpan<byte> signature, string publicKeyPath)
{
string publicKeyPem = File.ReadAllText(publicKeyPath);
using (MLDsa key = MLDsa.ImportFromPem(publicKeyPem))
{
return key.VerifyData(data, signature);
}
}
Au lieu de définir des propriétés d’objet et de voir une clé générée, la génération de clés pour ces nouveaux types utilise toutes les options requises.
using (MLKem key = MLKem.GenerateKey(MLKemAlgorithm.MLKem768))
{
string publicKeyPem = key.ExportSubjectPublicKeyInfoPem();
...
}
Ces algorithmes continuent tous avec le modèle d’avoir une propriété statique IsSupported pour indiquer si l’algorithme est pris en charge sur le système actuel.
.NET 10 inclut l’API de chiffrement Windows : prise en charge de la nouvelle génération (CNG) pour le chiffrement post-quantique (PQC), ce qui rend ces algorithmes disponibles sur les systèmes Windows avec prise en charge PQC. Par exemple:
using System;
using System.IO;
using System.Security.Cryptography;
private static bool ValidateMLDsaSignature(ReadOnlySpan<byte> data, ReadOnlySpan<byte> signature, string publicKeyPath)
{
string publicKeyPem = File.ReadAllText(publicKeyPath);
using MLDsa key = MLDsa.ImportFromPem(publicKeyPem);
return key.VerifyData(data, signature);
}
Les algorithmes PQC sont disponibles sur les systèmes où les bibliothèques de chiffrement système sont OpenSSL 3.5 (ou versions ultérieures) ou Windows CNG avec prise en charge de PQC. Le MLKem type n’est pas marqué comme [Experimental], mais certaines de ses méthodes le sont (et le seront jusqu’à ce que les normes sous-jacentes soient finalisées). Les classes MLDsa, SlhDsa et CompositeMLDsa sont marquées comme étant au stade de diagnostic [Experimental] jusqu'à ce que le développement SYSLIB5006 soit terminé.
ML-DSA
La MLDsa classe inclut des fonctionnalités de facilité d’utilisation qui simplifient les modèles de code courants :
private static byte[] SignData(string privateKeyPath, ReadOnlySpan<byte> data)
{
using (MLDsa signingKey = MLDsa.ImportFromPem(File.ReadAllBytes(privateKeyPath)))
{
- byte[] signature = new byte[signingKey.Algorithm.SignatureSizeInBytes];
- signingKey.SignData(data, signature);
+ return signingKey.SignData(data);
- return signature;
}
}
En outre, .NET 10 ajoute la prise en charge de HashML-DSA, qui est appelée « PreHash » pour vous aider à la distinguer de « pure » ML-DSA. Lorsque la spécification sous-jacente interagit avec la valeur OID (Object Identifier), les méthodes SignPreHash et VerifyPreHash sur ce [Experimental] type prennent l’OID en pointillés comme chaîne. Cela peut évoluer à mesure que d’autres scénarios utilisant HashML-DSA deviennent bien définis.
private static byte[] SignPreHashSha3_256(MLDsa signingKey, ReadOnlySpan<byte> data)
{
const string Sha3_256Oid = "2.16.840.1.101.3.4.2.8";
return signingKey.SignPreHash(SHA3_256.HashData(data), Sha3_256Oid);
}
À compter de RC 1, ML-DSA prend également en charge les signatures créées et vérifiées à partir d’une valeur mu « externe », ce qui offre une flexibilité supplémentaire pour les scénarios de chiffrement avancés :
private static byte[] SignWithExternalMu(MLDsa signingKey, ReadOnlySpan<byte> externalMu)
{
return signingKey.SignMu(externalMu);
}
private static bool VerifyWithExternalMu(MLDsa verifyingKey, ReadOnlySpan<byte> externalMu, ReadOnlySpan<byte> signature)
{
return verifyingKey.VerifyMu(externalMu, signature);
}
ML-DSA composite
.NET 10 introduit de nouveaux types pour prendre en charge ietf-lamps-pq-composite-sigs (au stade de l'ébauche 8 dans .NET 10 GA), y compris les types CompositeMLDsa et CompositeMLDsaAlgorithm, avec l’implémentation des méthodes primitives pour les versions de RSA.
var algorithm = CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss;
using var privateKey = CompositeMLDsa.GenerateKey(algorithm);
byte[] data = [42];
byte[] signature = privateKey.SignData(data);
using var publicKey = CompositeMLDsa.ImportCompositeMLDsaPublicKey(algorithm, privateKey.ExportCompositeMLDsaPublicKey());
Console.WriteLine(publicKey.VerifyData(data, signature)); // True
signature[0] ^= 1; // Tamper with signature
Console.WriteLine(publicKey.VerifyData(data, signature)); // False
AES KeyWrap avec Padding (IETF RFC 5649)
AES-KWP est un algorithme qui est parfois utilisé dans des constructions telles que la syntaxe de message de chiffrement (CMS) EnvelopedData, où le contenu est chiffré une seule fois, mais la clé de déchiffrement doit être distribuée à plusieurs parties, chacune dans un formulaire secret distinct.
.NET prend désormais en charge l’algorithme AES-KWP via des méthodes d’instance sur la Aes classe :
private static byte[] DecryptContent(ReadOnlySpan<byte> kek, ReadOnlySpan<byte> encryptedKey, ReadOnlySpan<byte> ciphertext)
{
using (Aes aes = Aes.Create())
{
aes.SetKey(kek);
Span<byte> dek = stackalloc byte[256 / 8];
int length = aes.DecryptKeyWrapPadded(encryptedKey, dek);
aes.SetKey(dek.Slice(0, length));
return aes.DecryptCbc(ciphertext);
}
}
Globalisation et date/heure
- Nouvelles surcharges de méthode dans ISOWeek pour le type DateOnly
- Classement numérique pour la comparaison de chaînes
-
Nouvelle surcharge
TimeSpan.FromMillisecondsavec un paramètre unique
Nouvelles surcharges de méthode dans ISOWeek pour le type DateOnly
La ISOWeek classe a été initialement conçue pour fonctionner exclusivement avec DateTime, car elle a été introduite avant l’existence du DateOnly type. Maintenant que DateOnly est disponible, il est judicieux que ISOWeek le prenne en charge également. Les surcharges suivantes sont nouvelles :
Classement numérique pour la comparaison de chaînes
La comparaison de chaînes numériques est une fonctionnalité hautement demandée pour comparer les chaînes numériquement au lieu de lexicographiquement. Par exemple, 2 est inférieur à 10, il doit donc "2" apparaître avant "10" lorsque l'on les classe numériquement. De même, "2" et "02" sont égaux numériquement. Avec la nouvelle NumericOrdering option, il est désormais possible d’effectuer ces types de comparaisons :
StringComparer numericStringComparer = StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering);
Console.WriteLine(numericStringComparer.Equals("02", "2"));
// Output: True
foreach (string os in new[] { "Windows 8", "Windows 10", "Windows 11" }.Order(numericStringComparer))
{
Console.WriteLine(os);
}
// Output:
// Windows 8
// Windows 10
// Windows 11
HashSet<string> set = new HashSet<string>(numericStringComparer) { "007" };
Console.WriteLine(set.Contains("7"));
// Output: True
Cette option n’est pas valide pour les opérations de chaîne basées sur l’index suivantes : IndexOf, , LastIndexOf, StartsWith, EndsWith, IsPrefixet IsSuffix.
Nouvelle surcharge TimeSpan.FromMilliseconds avec un paramètre unique
La TimeSpan.FromMilliseconds(Int64, Int64) méthode a été introduite précédemment sans ajouter de surcharge qui prend un seul paramètre.
Bien que cela fonctionne depuis que le deuxième paramètre est facultatif, il provoque une erreur de compilation lorsqu’elle est utilisée dans une expression LINQ comme :
Expression<Action> a = () => TimeSpan.FromMilliseconds(1000);
Le problème se produit, car les expressions LINQ ne peuvent pas gérer les paramètres facultatifs. Pour résoudre ce problème, .NET 10 introduit une nouvelle surcharge prend un paramètre unique. Il modifie également la méthode existante pour rendre le deuxième paramètre obligatoire.
Chaînes
- API de normalisation des chaînes pour travailler avec une plage de caractères
- Prise en charge de l'UTF-8 pour la conversion de chaînes hexadécimales
API de normalisation de chaînes pour travailler avec l'ensemble des caractères
La normalisation des chaînes Unicode a été prise en charge depuis longtemps, mais les API existantes ne fonctionnaient qu’avec le type de chaîne. Cela signifie que les appelants, associés à des données stockées sous différentes formes, telles que des tableaux de caractères ou des segments, doivent allouer une nouvelle chaîne pour pouvoir utiliser ces API. En outre, les API qui retournent une chaîne normalisée allouent toujours une nouvelle chaîne pour représenter la sortie normalisée.
.NET 10 introduit de nouvelles API qui fonctionnent avec des étendues de caractères, qui étendent la normalisation au-delà des types de chaînes et aident à éviter les allocations inutiles :
- StringNormalizationExtensions.GetNormalizedLength(ReadOnlySpan<Char>, NormalizationForm)
- StringNormalizationExtensions.IsNormalized(ReadOnlySpan<Char>, NormalizationForm)
- StringNormalizationExtensions.TryNormalize(ReadOnlySpan<Char>, Span<Char>, Int32, NormalizationForm)
Prise en charge d'UTF-8 pour la conversion de chaîne hexadécimale
.NET 10 ajoute la compatibilité UTF-8 pour les opérations de conversion de chaîne hexadécimale dans la classe Convert. Ces nouvelles méthodes permettent de convertir efficacement des séquences d’octets UTF-8 et des représentations hexadécimales sans nécessiter d’allocations de chaîne intermédiaires :
- Convert.FromHexString(ReadOnlySpan<Byte>)
- Convert.FromHexString(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32)
- Convert.TryToHexString(ReadOnlySpan<Byte>, Span<Byte>, Int32)
- Convert.TryToHexStringLower(ReadOnlySpan<Byte>, Span<Byte>, Int32)
Ces méthodes reflètent les surcharges existantes qui fonctionnent avec string et ReadOnlySpan<char>, mais fonctionnent directement sur des octets codés en UTF-8 pour améliorer les performances dans les scénarios où vous utilisez déjà des données UTF-8.
Collections
Surcharges TryAdd et TryGetValue supplémentaires pour OrderedDictionary<TKey, TValue>
OrderedDictionary<TKey,TValue> fournit TryAdd et TryGetValue pour l’ajout et la récupération comme toute autre IDictionary<TKey, TValue> implémentation. Toutefois, il existe des scénarios dans lesquels vous souhaiterez peut-être effectuer davantage d’opérations. De nouvelles surcharges sont donc ajoutées pour renvoyer un index à l’entrée :
Cet index peut ensuite être utilisé avec GetAt et SetAt pour un accès rapide à l’entrée. Un exemple d’utilisation de la nouvelle surcharge TryAdd consiste à ajouter ou mettre à jour une paire clé-valeur dans le dictionnaire ordonné :
// Try to add a new key with value 1.
if (!orderedDictionary.TryAdd(key, 1, out int index))
{
// Key was present, so increment the existing value instead.
int value = orderedDictionary.GetAt(index).Value;
orderedDictionary.SetAt(index, value + 1);
}
Cette nouvelle API est déjà utilisée dans JsonObject et améliore les performances pour la mise à jour des propriétés de 10 à 20 %.
Sérialisation
-
Autoriser la spécification de ReferenceHandler dans
JsonSourceGenerationOptions - Option pour interdire les propriétés JSON dupliquées
- Options de sérialisation JSON strictes
- Prise en charge de PipeReader pour le sérialiseur JSON
Autoriser la spécification de ReferenceHandler dans JsonSourceGenerationOptions
Lorsque vous utilisez des générateurs source pour la sérialisation JSON, le contexte généré lève une erreur lorsque les cycles sont sérialisés ou désérialisés. Vous pouvez maintenant personnaliser ce comportement en spécifiant le ReferenceHandler dans le JsonSourceGenerationOptionsAttribute. Voici un exemple utilisant JsonKnownReferenceHandler.Preserve:
public static void MakeSelfRef()
{
SelfReference selfRef = new SelfReference();
selfRef.Me = selfRef;
Console.WriteLine(JsonSerializer.Serialize(selfRef, ContextWithPreserveReference.Default.SelfReference));
// Output: {"$id":"1","Me":{"$ref":"1"}}
}
[JsonSourceGenerationOptions(ReferenceHandler = JsonKnownReferenceHandler.Preserve)]
[JsonSerializable(typeof(SelfReference))]
internal partial class ContextWithPreserveReference : JsonSerializerContext
{
}
internal class SelfReference
{
public SelfReference Me { get; set; } = null!;
}
Option pour interdire les propriétés JSON dupliquées
La spécification JSON ne spécifie pas comment gérer les propriétés dupliquées lors de la désérialisation d’une charge utile JSON. Cela peut entraîner des résultats inattendus et des vulnérabilités de sécurité. .NET 10 introduit l’option JsonSerializerOptions.AllowDuplicateProperties permettant de interdire les propriétés JSON dupliquées :
string json = """{ "Value": 1, "Value": -1 }""";
Console.WriteLine(JsonSerializer.Deserialize<MyRecord>(json).Value); // -1
JsonSerializerOptions options = new() { AllowDuplicateProperties = false };
JsonSerializer.Deserialize<MyRecord>(json, options); // throws JsonException
JsonSerializer.Deserialize<JsonObject>(json, options); // throws JsonException
JsonSerializer.Deserialize<Dictionary<string, int>>(json, options); // throws JsonException
JsonDocumentOptions docOptions = new() { AllowDuplicateProperties = false };
JsonDocument.Parse(json, docOptions); // throws JsonException
record MyRecord(int Value);
Les doublons sont détectés en vérifiant si une valeur est assignée plusieurs fois lors de la désérialisation, ce qui fonctionne comme prévu avec d’autres options telles que la sensibilité à la casse et la stratégie de dénomination.
Options de sérialisation JSON strictes
Le sérialiseur JSON accepte de nombreuses options pour personnaliser la sérialisation et la désérialisation, mais les valeurs par défaut peuvent être trop détendues pour certaines applications. .NET 10 ajoute une nouvelle JsonSerializerOptions.Strict présélection qui suit les meilleures pratiques en incluant les options suivantes :
- Applique la stratégie JsonUnmappedMemberHandling.Disallow.
- Désactive JsonSerializerOptions.AllowDuplicateProperties.
- Préserve la liaison des propriétés sensible à la casse.
- Active les paramètres de JsonSerializerOptions.RespectNullableAnnotations et de JsonSerializerOptions.RespectRequiredConstructorParameters.
Ces options sont compatibles en lecture avec JsonSerializerOptions.Default : un objet sérialisé avec JsonSerializerOptions.Default peut être désérialisé avec JsonSerializerOptions.Strict.
Pour plus d’informations sur la sérialisation JSON, consultez la vue d’ensemble de System.Text.Json.
Prise en charge de PipeReader pour le sérialiseur JSON
JsonSerializer.Deserialize prend désormais en charge PipeReader, en complément du support existant PipeWriter . Auparavant, la désérialisation d’une PipeReader conversion nécessaire en un Stream, mais les nouvelles surcharges éliminent cette étape en intégrant PipeReader directement dans le sérialiseur. En bonus, il n’est pas nécessaire de convertir ce que vous tenez déjà peut générer des avantages d’efficacité.
Cela montre l’utilisation de base :
using System;
using System.IO.Pipelines;
using System.Text.Json;
using System.Threading.Tasks;
var pipe = new Pipe();
// Serialize to writer
await JsonSerializer.SerializeAsync(pipe.Writer, new Person("Alice"));
await pipe.Writer.CompleteAsync();
// Deserialize from reader
var result = await JsonSerializer.DeserializeAsync<Person>(pipe.Reader);
await pipe.Reader.CompleteAsync();
Console.WriteLine($"Your name is {result.Name}.");
// Output: Your name is Alice.
record Person(string Name);
Voici un exemple de producteur qui produit des jetons en blocs et un consommateur qui les reçoit et les affiche :
using System;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Text.Json;
using System.Threading.Tasks;
var pipe = new Pipe();
// Producer writes to the pipe in chunks.
var producerTask = Task.Run(async () =>
{
async static IAsyncEnumerable<Chunk> GenerateResponse()
{
yield return new Chunk("The quick brown fox", DateTime.Now);
await Task.Delay(500);
yield return new Chunk(" jumps over", DateTime.Now);
await Task.Delay(500);
yield return new Chunk(" the lazy dog.", DateTime.Now);
}
await JsonSerializer.SerializeAsync<IAsyncEnumerable<Chunk>>(pipe.Writer, GenerateResponse());
await pipe.Writer.CompleteAsync();
});
// Consumer reads from the pipe and outputs to console.
var consumerTask = Task.Run(async () =>
{
var thinkingString = "...";
var clearThinkingString = new string("\b\b\b");
var lastTimestamp = DateTime.MinValue;
// Read response to end.
Console.Write(thinkingString);
await foreach (var chunk in JsonSerializer.DeserializeAsyncEnumerable<Chunk>(pipe.Reader))
{
Console.Write(clearThinkingString);
Console.Write(chunk.Message);
Console.Write(thinkingString);
lastTimestamp = DateTime.Now;
}
Console.Write(clearThinkingString);
Console.WriteLine($" Last message sent at {lastTimestamp}.");
await pipe.Reader.CompleteAsync();
});
await producerTask;
await consumerTask;
record Chunk(string Message, DateTime Timestamp);
Tout cela est sérialisé en tant que JSON dans le Pipe (mis en forme ici pour la lisibilité) :
[
{
"Message": "The quick brown fox",
"Timestamp": "2025-08-01T18:37:27.2930151-07:00"
},
{
"Message": " jumps over",
"Timestamp": "2025-08-01T18:37:27.8594502-07:00"
},
{
"Message": " the lazy dog.",
"Timestamp": "2025-08-01T18:37:28.3753669-07:00"
}
]
System.Numerics
- Autres méthodes de transformation de matrices en perspective gauche
- Améliorations apportées à Tensor
Autres méthodes de transformation de matrices en perspective gauche
.NET 10 ajoute les API restantes pour la création de matrices de transformation en perspective gauche pour les matrices de billboard et de billboard contraints. Vous pouvez utiliser ces méthodes comme leurs équivalents droitiers existants, par exemple, CreateBillboard(Vector3, Vector3, Vector3, Vector3)lors de l’utilisation d’un système de coordonnées gaucher à la place :
- Matrix4x4.CreateBillboardLeftHanded(Vector3, Vector3, Vector3, Vector3)
- Matrix4x4.CreateConstrainedBillboardLeftHanded(Vector3, Vector3, Vector3, Vector3, Vector3)
Améliorations apportées à Tensor
L’espace de noms System.Numerics.Tensors inclut désormais une interface non générique, IReadOnlyTensor, pour des opérations telles que l'accès à Lengths et Strides. Les opérations de tranche ne copient plus les données, ce qui améliore les performances. En outre, vous pouvez accéder aux données de façon spécifique avec l’encapsulation object lorsque les performances ne sont pas critiques.
Les API de tenseur sont désormais stables et ne sont plus marquées comme expérimentales. Bien que les API nécessitent toujours de référencer le package NuGet System.Numerics.Tensors , elles ont été soigneusement examinées et finalisées pour la version .NET 10. Les types tirent parti des opérateurs d’extension C# 14 pour fournir des opérations arithmétiques lorsque le type T sous-jacent prend en charge l’opération. Si T implémente les interfaces mathématiques génériques pertinentes, par exemple, IAdditionOperators<TSelf, TOther, TResult> ou INumber<TSelf>, l’opération est prise en charge. Par exemple, tensor + tensor est disponible pour un Tensor<int>, mais n’est pas disponible pour un Tensor<bool>.
Validation des options
Nouveau constructeur compatible AOT pour ValidationContext
La ValidationContext classe, utilisée pendant la validation des options, inclut une nouvelle surcharge de constructeur qui accepte explicitement le displayName paramètre :
ValidationContext(Object, String, IServiceProvider, IDictionary<Object,Object>)
Le nom d’affichage garantit la sécurité AOT et active son utilisation dans les builds natives sans avertissements.
Diagnostiques
-
Prise en charge des URL de schéma de télémétrie dans
ActivitySourceetMeter - Prise en charge des traces hors processus pour les événements d’activité et les liens
- Prise en charge de l’échantillonnage des traces avec limitation du débit
Prise en charge des URL de schéma de télémétrie dans ActivitySource et Meter
ActivitySource et Meter prend désormais en charge la spécification d’une URL de schéma de télémétrie pendant la construction, qui s’aligne sur les spécifications OpenTelemetry. Le schéma de télémétrie garantit la cohérence et la compatibilité des données de suivi et de métriques. En outre, .NET 10 introduit ActivitySourceOptions, ce qui simplifie la création d’instances avec plusieurs options de ActivitySource configuration (y compris l’URL du schéma de télémétrie).
Les nouvelles API sont les suivantes :
- ActivitySource(ActivitySourceOptions)
- ActivitySource.TelemetrySchemaUrl
- Meter.TelemetrySchemaUrl
- ActivitySourceOptions
Prise en charge des traces hors processus pour les événements d’activité et les liens
La Activity classe active le suivi distribué en suivant le flux d’opérations entre les services ou les composants. .NET prend en charge la sérialisation de ces données de suivi hors processus via le fournisseur de source d’événements Microsoft-Diagnostics-DiagnosticSource . Un Activity peut inclure des métadonnées supplémentaires telles que ActivityLink et ActivityEvent. .NET 10 ajoute la prise en charge de la sérialisation de ces liens et événements, de sorte que les données de trace hors processus incluent désormais ces informations. Par exemple:
Events->"[(TestEvent1,2025-03-27T23:34:10.6225721+00:00,[E11:EV1,E12:EV2]),(TestEvent2,2025-03-27T23:34:11.6276895+00:00,[E21:EV21,E22:EV22])]"
Links->"[(19b6e8ea216cb2ba36dd5d957e126d9f,98f7abcb3418f217,Recorded,null,false,[alk1:alv1,alk2:alv2]),(2d409549aadfdbdf5d1892584a5f2ab2,4f3526086a350f50,None,null,false)]"
Prise en charge de l’échantillonnage des traces avec limitation du débit
Lorsque les données de suivi distribuées sont sérialisées hors processus via le Microsoft-Diagnostics-DiagnosticSource fournisseur de source d’événements, toutes les activités enregistrées peuvent être émises ou l’échantillonnage peut être appliqué en fonction d’un ratio de trace.
Une nouvelle option d’échantillonnage appelée Rate Limiting Sampling limite le nombre d’activités racines sérialisées par seconde. Cela permet de contrôler plus précisément le volume de données.
Les agrégateurs de données de trace hors processus peuvent activer et configurer cet échantillonnage en spécifiant l’option dans FilterAndPayloadSpecs. Par exemple, le paramètre suivant limite la sérialisation à 100 activités racines par seconde dans toutes les ActivitySource instances :
[AS]*/-ParentRateLimitingSampler(100)
Fichiers ZIP
- Améliorations des performances et de la mémoire ZipArchive
- Nouvelles API ZIP asynchrones
- Amélioration des performances dans GZipStream pour les flux concaténés
Améliorations des performances et de la mémoire ZipArchive
.NET 10 améliore les performances et l’utilisation de la mémoire de ZipArchive.
Tout d’abord, la façon dont les entrées sont écrites dans un ZipArchive lorsque le mode Update est activé a été optimisée. Auparavant, toutes les instances ZipArchiveEntry étaient chargées en mémoire et réécrites, ce qui pouvait entraîner une utilisation élevée de la mémoire et des goulots d’étranglement des performances. L’optimisation réduit l’utilisation de la mémoire et améliore les performances en évitant la nécessité de charger toutes les entrées en mémoire.
Deuxièmement, l’extraction des entrées est désormais parallélisée et les structures de données internes sont optimisées pour une meilleure utilisation de ZipArchive la mémoire. Ces améliorations résolvent les problèmes liés aux goulots d’étranglement des performances et à l’utilisation élevée de la mémoire, ce qui rend ZipArchive plus efficace et plus rapide, en particulier lorsqu’il s’agit d’archives volumineuses.
Nouvelles API ZIP asynchrones
.NET 10 introduit de nouvelles API asynchrones qui facilitent l’exécution d’opérations non bloquantes lors de la lecture ou de l’écriture dans des fichiers ZIP. Cette fonctionnalité a été fortement demandée par la communauté.
De nouvelles async méthodes sont disponibles pour extraire, créer et mettre à jour des archives ZIP. Ces méthodes permettent aux développeurs de gérer efficacement les fichiers volumineux et d’améliorer la réactivité des applications, en particulier dans les scénarios impliquant des opérations liées aux E/S. Ces méthodes sont les suivantes :
- ZipArchive.CreateAsync(Stream, ZipArchiveMode, Boolean, Encoding, CancellationToken)
- ZipArchiveEntry.OpenAsync(CancellationToken)
- ZipFile.CreateFromDirectoryAsync
- ZipFile.ExtractToDirectoryAsync
- ZipFile.OpenAsync
- ZipFile.OpenReadAsync(String, CancellationToken)
- ZipFileExtensions.CreateEntryFromFileAsync
- ZipFileExtensions.ExtractToDirectoryAsync
- ZipFileExtensions.ExtractToFileAsync
Pour obtenir des exemples d’utilisation de ces API, consultez le billet de blog Preview 4.
Amélioration des performances dans GZipStream pour les flux concaténés
Une contribution de la communauté a amélioré les performances du traitement des flux de GZipStream données GZip concaténés. Auparavant, chaque nouveau segment de flux libérait et réallouait l’élément interne ZLibStreamHandle, ce qui entraînait des allocations de mémoire supplémentaires et une surcharge d’initialisation. Avec cette modification, le handle est maintenant réinitialisé et réutilisé pour réduire les allocations de mémoire managées et non managées et améliorer le temps d’exécution. L’impact le plus important (environ 35% plus rapide) est observé lors du traitement d’un grand nombre de petits flux de données. Cette modification :
- Élimine l’allocation répétée d’environ 64 à 80 octets de mémoire par flux concaténé, avec des économies de mémoire non managées supplémentaires.
- Réduit le temps d’exécution d’environ 400 ns par flux concaténé.
Gestion des processus Windows
Lancer des processus Windows dans un nouveau groupe de processus
Pour Windows, vous pouvez désormais utiliser ProcessStartInfo.CreateNewProcessGroup pour lancer un processus dans un groupe de processus distinct. Cela vous permet d’envoyer des signaux isolés aux processus enfants qui pourraient autrement retirer le parent sans gestion appropriée. L’envoi de signaux est pratique pour éviter l’arrêt forcé.
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
class Program
{
static void Main(string[] args)
{
bool isChildProcess = args.Length > 0 && args[0] == "child";
if (!isChildProcess)
{
var psi = new ProcessStartInfo
{
FileName = Environment.ProcessPath,
Arguments = "child",
CreateNewProcessGroup = true,
};
using Process process = Process.Start(psi)!;
Thread.Sleep(5_000);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, (uint)process.Id);
process.WaitForExit();
Console.WriteLine("Child process terminated gracefully, continue with the parent process logic if needed.");
}
else
{
// If you need to send a CTRL+C, the child process needs to re-enable CTRL+C handling, if you own the code, you can call SetConsoleCtrlHandler(NULL, FALSE).
// see https://learn.microsoft.com/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks
SetConsoleCtrlHandler((IntPtr)null, false);
Console.WriteLine("Greetings from the child process! I need to be gracefully terminated, send me a signal!");
bool stop = false;
var registration = PosixSignalRegistration.Create(PosixSignal.SIGINT, ctx =>
{
stop = true;
ctx.Cancel = true;
Console.WriteLine("Received CTRL+C, stopping...");
});
StreamWriter sw = File.AppendText("log.txt");
int i = 0;
while (!stop)
{
Thread.Sleep(1000);
sw.WriteLine($"{++i}");
Console.WriteLine($"Logging {i}...");
}
// Clean up
sw.Dispose();
registration.Dispose();
Console.WriteLine("Thanks for not killing me!");
}
}
private const int CTRL_C_EVENT = 0;
private const int CTRL_BREAK_EVENT = 1;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleCtrlHandler(IntPtr handler, [MarshalAs(UnmanagedType.Bool)] bool Add);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId);
}
Améliorations de WebSocket
WebSocketStream
.NET 10 introduit WebSocketStream, une nouvelle API conçue pour simplifier certains des scénarios les plus courants et précédemment fastidieux dans WebSocket .NET.
Les API traditionnelles WebSocket sont de bas niveau et nécessitent une mise en mémoire tampon importante : gestion de la mise en mémoire tampon et de l’encadrement, reconstruction des messages, gestion de l’encodage/décodage et écriture de wrappers personnalisés pour s’intégrer à des flux, des canaux ou d’autres abstractions de transport. Ces complexités rendent difficile l’utilisation de WebSockets comme un transport, en particulier pour les applications avec des protocoles basés sur le streaming ou du texte, ou des gestionnaires pilotés par les événements.
WebSocketStream traite ces points de douleur en fournissant une Streamabstraction basée sur un WebSocket. Cela permet une intégration transparente avec les API existantes pour la lecture, l’écriture et l’analyse des données, qu’elles soient binaires ou textuelles, et réduit le besoin de plomberie manuelle.
WebSocketStream permet des API de haut niveau et familières pour les modèles courants de consommation et de production de WebSocket. Ces API réduisent les frictions et facilitent l’implémentation de scénarios avancés.
Modèles d’utilisation courants
Voici quelques exemples de la façon dont WebSocketStream simplifie les flux de travail classiques WebSocket :
Protocole de texte de streaming (par exemple, STOMP)
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
// Streaming text protocol (for example, STOMP).
using Stream transportStream = WebSocketStream.Create(
connectedWebSocket,
WebSocketMessageType.Text,
ownsWebSocket: true);
// Integration with Stream-based APIs.
// Don't close the stream, as it's also used for writing.
using var transportReader = new StreamReader(transportStream, leaveOpen: true);
var line = await transportReader.ReadLineAsync(cancellationToken); // Automatic UTF-8 and new line handling.
transportStream.Dispose(); // Automatic closing handshake handling on `Dispose`.
Protocole binaire de streaming (par exemple, AMQP)
using System;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
// Streaming binary protocol (for example, AMQP).
Stream transportStream = WebSocketStream.Create(
connectedWebSocket,
WebSocketMessageType.Binary,
closeTimeout: TimeSpan.FromSeconds(10));
await message.SerializeToStreamAsync(transportStream, cancellationToken);
var receivePayload = new byte[payloadLength];
await transportStream.ReadExactlyAsync(receivePayload, cancellationToken);
transportStream.Dispose();
// `Dispose` automatically handles closing handshake.
Lire un seul message en tant que flux (par exemple, désérialisation JSON)
using System.IO;
using System.Net.WebSockets;
using System.Text.Json;
// Reading a single message as a stream (for example, JSON deserialization).
using Stream messageStream = WebSocketStream.CreateReadableMessageStream(connectedWebSocket, WebSocketMessageType.Text);
// JsonSerializer.DeserializeAsync reads until the end of stream.
var appMessage = await JsonSerializer.DeserializeAsync<AppMessage>(messageStream);
Écrire un seul message en tant que flux (par exemple, sérialisation binaire)
using System;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
// Writing a single message as a stream (for example, binary serialization).
public async Task SendMessageAsync(AppMessage message, CancellationToken cancellationToken)
{
using Stream messageStream = WebSocketStream.CreateWritableMessageStream(_connectedWebSocket, WebSocketMessageType.Binary);
foreach (ReadOnlyMemory<byte> chunk in message.SplitToChunks())
{
await messageStream.WriteAsync(chunk, cancellationToken);
}
} // EOM sent on messageStream.Dispose().
Améliorations de TLS
TLS 1.3 pour macOS (client)
.NET 10 ajoute la prise en charge de TLS 1.3 côté client sur macOS en intégrant Network.framework d’Apple dans SslStream et HttpClient. Historiquement, macOS a utilisé le transport sécurisé qui ne prend pas en charge TLS 1.3 ; l’adhésion à Network.framework active TLS 1.3.
Étendue et comportement
- macOS uniquement côté client dans cette version.
- Opt-in. Les applications existantes continuent d’utiliser la pile actuelle, sauf si elles sont activées.
- Lorsque cette option est activée, les versions TLS antérieures (TLS 1.0 et 1.1) peuvent ne plus être disponibles via Network.framework.
Comment activer
Utilisez un commutateur AppContext dans le code :
// Opt in to Network.framework-backed TLS on Apple platforms.
AppContext.SetSwitch("System.Net.Security.UseNetworkFramework", true);
using var client = new HttpClient();
var html = await client.GetStringAsync("https://example.com");
Vous pouvez également utiliser une variable d’environnement :
# Opt-in via environment variable (set for the process or machine as appropriate)
DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=1
# or
DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK=true
Remarques
- TLS 1.3 s’applique aux SslStream API basées sur celle-ci (par exemple). HttpClient/HttpMessageHandler
- Les suites de chiffrement sont contrôlées par macOS via Network.framework.
- Le comportement du flux sous-jacent peut différer lorsque Network.framework est activé (par exemple, mise en mémoire tampon, saisie semi-automatique en lecture/écriture, sémantique d’annulation).
- La sémantique peut différer pour les lectures de zéro octet. Évitez de compter sur des lectures de longueur nulle pour détecter la disponibilité des données.
- Certains noms d’hôte de noms de domaine internationalisés (IDN) peuvent être rejetés par Network.framework. Préférez les noms d’hôte ASCII/Punycode (A-label) ou validez les noms par rapport aux contraintes macOS/Network.framework.
- Si votre application s’appuie sur un comportement de périphérie spécifique SslStream , validez-la sous Network.framework.