Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se describen las nuevas características de las bibliotecas de .NET para .NET 10.
Criptografía
- Buscar certificados por huellas digitales distintas de SHA-1
- Búsqueda de datos codificados en PEM en ASCII/UTF-8
- Algoritmo de cifrado para la exportación PKCS#12/PFX
- Criptografía posterior a la cuántica (PQC)
Buscar certificados por huellas digitales distintas de SHA-1
Buscar certificados de forma única por huella digital es una operación bastante común, pero el X509Certificate2Collection.Find(X509FindType, Object, Boolean) método (para el FindByThumbprint modo) solo busca el valor de huella digital SHA-1.
Existe cierto riesgo de usar el método Find para buscar huellas SHA-2-256 ("SHA256") y SHA-3-256, ya que estos algoritmos hash tienen las mismas longitudes.
En su lugar, .NET 10 presenta un nuevo método que acepta el nombre del algoritmo hash que se va a usar para la coincidencia.
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();
Búsqueda de datos codificados en PEM en ASCII/UTF-8
La codificación PEM (originalmente Correo mejorado de privacidad, pero ahora se usa ampliamente fuera del correo electrónico) se define para "texto", lo que significa que la PemEncoding clase se diseñó para ejecutarse en String y ReadOnlySpan<char>. Sin embargo, es habitual (especialmente en Linux) tener algo parecido a un certificado escrito en un archivo que usa la codificación ASCII (cadena). Históricamente, eso significaba que necesitaba abrir el archivo y convertir los bytes en caracteres (o una cadena) antes de poder usar PemEncoding.
El nuevo PemEncoding.FindUtf8(ReadOnlySpan<Byte>) método aprovecha el hecho de que PEM solo está definido para caracteres ASCII de 7 bits y que ASCII de 7 bits tiene una superposición perfecta con valores UTF-8 de un solo byte. Al llamar a este nuevo método, puede omitir la conversión UTF-8/ASCII-to-char y leer el archivo directamente.
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]);
Algoritmo de cifrado para la exportación PKCS#12/PFX
Los nuevos ExportPkcs12 métodos de X509Certificate permiten que los llamadores elijan qué algoritmos de cifrado y resumen se usan para generar la salida:
- Pkcs12ExportPbeParameters.Pkcs12TripleDesSha1 indica el estándar de facto de la era de Windows XP. Genera una salida compatible con casi todas las bibliotecas y plataformas que admiten la lectura de PKCS#12/PFX eligiendo un algoritmo de cifrado anterior.
- Pkcs12ExportPbeParameters.Pbes2Aes256Sha256 indica que se debe usar AES en lugar de 3DES (y SHA-2-256 en lugar de SHA-1), pero es posible que todos los lectores (como Windows XP) no comprendan la salida.
Si desea tener aún más control, puede utilizar la sobrecarga que admite un PbeParameters.
Criptografía posterior a la cuántica (PQC)
.NET 10 incluye compatibilidad con tres nuevos algoritmos asimétricos: ML-KEM (FIPS 203), ML-DSA (FIPS 204) y SLH-DSA (FIPS 205). Los nuevos tipos son:
- System.Security.Cryptography.MLKem
- System.Security.Cryptography.MLDsa
- System.Security.Cryptography.SlhDsa
Dado que agrega poca ventaja, estos nuevos tipos no derivan de AsymmetricAlgorithm.
AsymmetricAlgorithm En lugar del enfoque de crear un objeto y, a continuación, importar una clave en él, o generar una clave nueva, los nuevos tipos usan métodos estáticos para generar o importar una clave:
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);
}
}
En lugar de establecer propiedades de objeto para que una clave se materialice, la generación de claves en estos nuevos tipos incorpora todas las opciones necesarias.
using (MLKem key = MLKem.GenerateKey(MLKemAlgorithm.MLKem768))
{
string publicKeyPem = key.ExportSubjectPublicKeyInfoPem();
...
}
Todos estos algoritmos continúan con el patrón de tener una propiedad estática IsSupported para indicar si el algoritmo se admite en el sistema actual.
.NET 10 incluye la compatibilidad de La API de criptografía de Windows: Next Generation (CNG) para la criptografía post cuántica (PQC), que hace que estos algoritmos estén disponibles en sistemas Windows con compatibilidad con PQC. Por ejemplo:
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);
}
Los algoritmos PQC están disponibles en sistemas en los que las bibliotecas criptográficas del sistema son OpenSSL 3.5 (o posterior) o CNG de Windows con compatibilidad con PQC. El MLKem tipo no está marcado como [Experimental], pero algunos de sus métodos son (y serán hasta que se finalicen los estándares subyacentes). Las clases MLDsa, SlhDsa y CompositeMLDsa están marcadas como [Experimental] bajo diagnóstico SYSLIB5006 hasta que se complete el desarrollo.
ML-DSA
La MLDsa clase incluye características fáciles de usar que simplifican los patrones de código comunes:
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;
}
}
Además, .NET 10 agrega compatibilidad con HashML-DSA, que se denomina "PreHash" para ayudar a distinguirlo de ML-DSA "puro". A medida que la especificación subyacente interactúa con el valor de Identificador de objeto (OID), los métodos SignPreHash y VerifyPreHash en este [Experimental] tipo toman el OID decimal de puntos como una cadena. Esto puede evolucionar a medida que más escenarios que usan HashML-DSA se vuelven bien definidos.
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);
}
A partir de RC 1, ML-DSA también admite firmas creadas y comprobadas a partir de un valor mu "externo", lo que proporciona flexibilidad adicional para escenarios criptográficos avanzados:
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 compuestos
.NET 10 presenta nuevos tipos para admitir ietf-lamps-pq-composite-sigs (en el borrador 8 a partir de .NET 10 GA), incluidos los tipos CompositeMLDsa y CompositeMLDsaAlgorithm, con la implementación de los métodos primitivos para variantes 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 with Padding (IETF RFC 5649)
AES-KWP es un algoritmo que se usa ocasionalmente en construcciones como Cryptographic Message Syntax (CMS) EnvelopedData, donde el contenido se cifra una vez, pero la clave de descifrado debe distribuirse a varias partes, cada una de ellas en un formato secreto distinto.
.NET ahora admite el algoritmo de AES-KWP a través de métodos de instancia en la Aes clase :
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);
}
}
Globalización y fecha y hora
- Nuevas sobrecargas de método en ISOWeek para el tipo DateOnly
- Ordenación numérica para la comparación de cadenas
-
Nueva
TimeSpan.FromMillisecondssobrecarga con un único parámetro
Nuevas sobrecargas de método en ISOWeek para el tipo DateOnly
La ISOWeek clase se diseñó originalmente para trabajar exclusivamente con DateTime, ya que se introdujo antes de que existiera el DateOnly tipo. Ahora que DateOnly está disponible, tiene sentido ISOWeek admitirlo también. Las siguientes sobrecargas son nuevas:
Ordenación numérica para la comparación de cadenas
La comparación numérica de cadenas es una característica muy solicitada para comparar cadenas numéricamente en lugar de lexicográficamente. Por ejemplo, 2 es menor que 10, por lo que "2" debe aparecer antes "10" cuando se ordenan numéricamente. De forma similar, "2" y "02" son iguales numéricamente. Con la nueva NumericOrdering opción, ahora es posible realizar estos tipos de comparaciones:
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
Esta opción no es válida para las siguientes operaciones de cadena basadas en índices: IndexOf, LastIndexOf, StartsWith, EndsWith, IsPrefixy IsSuffix.
Nueva sobrecarga TimeSpan.FromMilliseconds con un único parámetro
El TimeSpan.FromMilliseconds(Int64, Int64) método se introdujo anteriormente sin agregar una sobrecarga que toma un único parámetro.
Aunque esto funciona desde que el segundo parámetro es opcional, provoca un error de compilación cuando se usa en una expresión LINQ como:
Expression<Action> a = () => TimeSpan.FromMilliseconds(1000);
El problema se produce porque las expresiones LINQ no pueden controlar parámetros opcionales. Para solucionar esto, .NET 10 presenta una nueva sobrecarga que toma un único parámetro. También modifica el método existente para que el segundo parámetro sea obligatorio.
Cadenas
- API de normalización de cadenas para trabajar con intervalo de caracteres
- Compatibilidad con UTF-8 para la conversión de cadenas hexadecimales
APIs de normalización de cadenas para trabajar con rango de caracteres
La normalización de cadenas Unicode se ha admitido durante mucho tiempo, pero las API existentes solo funcionaban con el tipo de cadena. Esto significa que los autores de llamadas con datos almacenados en diferentes formas, como matrices de caracteres o intervalos, deben asignar una nueva cadena para usar estas API. Además, las API que devuelven una cadena normalizada siempre asignan una nueva cadena para representar la salida normalizada.
.NET 10 presenta nuevas API que funcionan con intervalos de caracteres, que amplían la normalización más allá de los tipos de cadena y ayudan a evitar asignaciones innecesarias:
- StringNormalizationExtensions.GetNormalizedLength(ReadOnlySpan<Char>, NormalizationForm)
- StringNormalizationExtensions.IsNormalized(ReadOnlySpan<Char>, NormalizationForm)
- StringNormalizationExtensions.TryNormalize(ReadOnlySpan<Char>, Span<Char>, Int32, NormalizationForm)
Compatibilidad con UTF-8 para la conversión de cadenas hexadecimales
.NET 10 agrega compatibilidad con UTF-8 para operaciones de conversión hexadecimal en la Convert clase . Estos nuevos métodos proporcionan formas eficaces de convertir entre secuencias de bytes UTF-8 y representaciones hexadecimales sin necesidad de asignaciones de cadenas intermedias:
- 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)
Estos métodos reflejan las sobrecargas existentes que funcionan con string y ReadOnlySpan<char>, pero funcionan directamente en bytes codificados UTF-8 para mejorar el rendimiento en escenarios en los que ya está trabajando con datos UTF-8.
Colecciones
Sobrecargas TryAdd y TryGetValue adicionales para OrderedDictionary<TKey, TValue>
OrderedDictionary<TKey,TValue> proporciona TryAdd y TryGetValue para agregar y recuperar como cualquier otra implementación de IDictionary<TKey, TValue>. Sin embargo, hay escenarios en los que es posible que desee realizar más operaciones, por lo que se agregan nuevas sobrecargas que devuelven un índice a la entrada:
A continuación, este índice se puede usar con GetAt y SetAt para acceder rápidamente a la entrada. Un uso de ejemplo de la nueva sobrecarga TryAdd es añadir o actualizar un par de clave-valor en el diccionario ordenado:
// 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);
}
Esta nueva API ya se usa en JsonObject y mejora el rendimiento de la actualización de propiedades en un 10-20 %.
Serialización
-
Permitir especificar ReferenceHandler en
JsonSourceGenerationOptions - Opción para no permitir propiedades JSON duplicadas
- Opciones estrictas de serialización JSON
- Compatibilidad de PipeReader con serializador JSON
Permitir especificar ReferenceHandler en JsonSourceGenerationOptions
Cuando se usan generadores de código para la serialización JSON, el contexto generado inicia una excepción al serializar o deserializar los ciclos. Ahora puede personalizar este comportamiento especificando el ReferenceHandler en el JsonSourceGenerationOptionsAttribute. Este es un ejemplo mediante 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!;
}
Opción para no permitir propiedades JSON duplicadas
La especificación JSON no especifica cómo controlar las propiedades duplicadas al deserializar una carga JSON. Esto puede provocar resultados inesperados y vulnerabilidades de seguridad. .NET 10 presenta la JsonSerializerOptions.AllowDuplicateProperties opción de no permitir propiedades JSON duplicadas:
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);
Los duplicados se detectan comprobando si se asigna un valor varias veces durante la deserialización, por lo que funciona según lo previsto con otras opciones, como la distinción de mayúsculas y minúsculas y la directiva de nomenclatura.
Opciones estrictas de serialización JSON
El serializador JSON acepta muchas opciones para personalizar la serialización y la deserialización, pero los valores predeterminados pueden ser demasiado relajados para algunas aplicaciones. .NET 10 agrega un nuevo JsonSerializerOptions.Strict valor preestablecido que sigue los procedimientos recomendados mediante la inclusión de las siguientes opciones:
- Aplica la JsonUnmappedMemberHandling.Disallow directiva.
- Deshabilita JsonSerializerOptions.AllowDuplicateProperties.
- Conserva la vinculación de propiedades sensible a mayúsculas y minúsculas.
- Habilita tanto la configuración JsonSerializerOptions.RespectNullableAnnotations como JsonSerializerOptions.RespectRequiredConstructorParameters.
Estas opciones son compatibles con la lectura con JsonSerializerOptions.Default: un objeto serializado con JsonSerializerOptions.Default se puede deserializar con JsonSerializerOptions.Strict.
Para obtener más información sobre la serialización JSON, consulte Introducción a System.Text.Json.
Compatibilidad de PipeReader con serializador JSON
JsonSerializer.Deserialize ahora admite PipeReader, complementando la compatibilidad existente PipeWriter . Anteriormente, deserializar de un PipeReader elemento necesario convertirlo en , Streampero las nuevas sobrecargas eliminan ese paso mediante la integración PipeReader directa en el serializador. Como bonificación, no tener que convertir de lo que ya está manteniendo puede producir algunas ventajas de eficiencia.
Esto muestra el uso básico:
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);
Este es un ejemplo de un productor que genera tokens en fragmentos y un consumidor que recibe y los muestra:
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);
Todo esto se serializa como JSON en Pipe (con el formato aquí para mejorar la legibilidad):
[
{
"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
Otros métodos de transformación de matrices para sistemas zurdos
.NET 10 añade las API restantes para crear matrices de transformación de sistemas zurdos para matrices de cartelera y de cartelera restringida. Puede usar estos métodos al igual que sus homólogos diestros existentes, por ejemplo, CreateBillboard(Vector3, Vector3, Vector3, Vector3), cuando utiliza un sistema de coordenadas zurdo en su lugar.
- Matrix4x4.CreateBillboardLeftHanded(Vector3, Vector3, Vector3, Vector3)
- Matrix4x4.CreateConstrainedBillboardLeftHanded(Vector3, Vector3, Vector3, Vector3, Vector3)
Mejoras de Tensor
El System.Numerics.Tensors espacio de nombres ahora incluye una interfaz no genérica, IReadOnlyTensor, para operaciones como el acceso a Lengths y Strides. Las operaciones de segmento ya no copian datos, lo que mejora el rendimiento. Además, se puede acceder a los datos de manera no genérica mediante boxing a object cuando el rendimiento no es crítico.
Las API de tensor ahora son estables y ya no están marcadas como experimentales. Aunque las API siguen necesitando hacer referencia al paquete NuGet System.Numerics.Tensors , se han revisado y finalizado exhaustivamente para la versión de .NET 10. Los tipos aprovechan los operadores de extensión de C# 14 para proporcionar operaciones aritméticas cuando el tipo T subyacente admite la operación. Si T implementa las interfaces matemáticas genéricas pertinentes, por ejemplo, IAdditionOperators<TSelf, TOther, TResult> o INumber<TSelf>, se admite la operación. Por ejemplo, tensor + tensor está disponible para Tensor<int>, pero no está disponible para Tensor<bool>.
Validación de opciones
Nuevo constructor seguro para AOT para ValidationContext
La ValidationContext clase , que se usa durante la validación de opciones, incluye una nueva sobrecarga de constructor que acepta explícitamente el displayName parámetro :
ValidationContext(Object, String, IServiceProvider, IDictionary<Object,Object>)
El nombre visible garantiza la seguridad de AOT y permite su uso en compilaciones nativas sin advertencias.
Diagnósticos
-
Compatibilidad con URL de esquema de telemetría en
ActivitySourceyMeter - Soporte de seguimiento fuera de proceso para eventos y enlaces de Actividad
- Compatibilidad con el muestreo de seguimiento de límite de velocidad
Compatibilidad con direcciones URL de esquema de telemetría en ActivitySource y Meter
ActivitySource y Meter ahora admite la especificación de una dirección URL de esquema de telemetría durante la construcción, que se alinea con las especificaciones de OpenTelemetry. El esquema de telemetría garantiza la coherencia y la compatibilidad con los datos de seguimiento y métricas. Además, .NET 10 presenta ActivitySourceOptions, que simplifica la creación de instancias con varias opciones de ActivitySource configuración (incluida la dirección URL del esquema de telemetría).
Las nuevas API son:
- ActivitySource(ActivitySourceOptions)
- ActivitySource.TelemetrySchemaUrl
- Meter.TelemetrySchemaUrl
- ActivitySourceOptions
Soporte de seguimiento fuera de proceso para eventos y enlaces de Actividad
La Activity clase habilita el seguimiento distribuido mediante el seguimiento del flujo de operaciones entre servicios o componentes. .NET admite la serialización de estos datos de seguimiento fuera del proceso a través del proveedor de origen de Microsoft-Diagnostics-DiagnosticSource eventos. Puede Activity incluir metadatos adicionales, como ActivityLink y ActivityEvent. .NET 10 agrega compatibilidad para serializar estos vínculos y eventos, por lo que los datos de seguimiento fuera de proceso ahora incluyen esa información. Por ejemplo:
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)]"
Compatibilidad con el muestreo de seguimiento de límite de velocidad
Cuando los datos de seguimiento distribuidos se serializan fuera del proceso a través del proveedor de origen de evento Microsoft-Diagnostics-DiagnosticSources, se pueden emitir todas las actividades grabadas o se puede aplicar el muestreo en función de una proporción de traza.
Una nueva opción de muestreo denominada Muestreo de limitación de velocidad restringe el número de actividades raíz serializadas por segundo. Esto ayuda a controlar el volumen de datos de forma más precisa.
Los agregadores de datos de seguimiento fuera de proceso pueden habilitar y configurar este muestreo especificando la opción en FilterAndPayloadSpecs. Por ejemplo, la siguiente configuración limita la serialización a 100 actividades raíz por segundo en todas las ActivitySource instancias:
[AS]*/-ParentRateLimitingSampler(100)
Archivos ZIP
- Mejoras en el rendimiento y la memoria de ZipArchive
- Nuevas API ZIP asincrónicas
- Mejora del rendimiento en GZipStream para secuencias concatenadas
Mejoras en el rendimiento y la memoria de ZipArchive
.NET 10 mejora el rendimiento y el uso de memoria de ZipArchive.
En primer lugar, se ha optimizado la forma en que las entradas se escriben en un ZipArchive cuando está en modo Update. Antes, todas las instancias de ZipArchiveEntry se cargaban en la memoria y se reescribían, lo que podría provocar un uso elevado de la memoria y cuellos de botella en el rendimiento. La optimización reduce el uso de memoria y mejora el rendimiento evitando la necesidad de cargar todas las entradas en la memoria.
En segundo lugar, la extracción de las entradas de ZipArchive ahora está paralelizada y las estructuras de datos internas están optimizadas para mejorar el uso de memoria. Estas mejoras abordan problemas relacionados con cuellos de botella de rendimiento y un uso elevado de memoria, lo que hace ZipArchive que sea más eficaz y más rápido, especialmente cuando se trabaja con archivos grandes.
Nuevas API ZIP asincrónicas
.NET 10 presenta nuevas API asincrónicas que facilitan la realización de operaciones sin bloqueo al leer o escribir en archivos ZIP. Esta característica fue muy solicitada por la comunidad.
Hay nuevos async métodos disponibles para extraer, crear y actualizar archivos ZIP. Estos métodos permiten a los desarrolladores controlar de forma eficaz archivos grandes y mejorar la capacidad de respuesta de la aplicación, especialmente en escenarios que implican operaciones enlazadas a E/S. Estos métodos incluyen:
- 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
Para obtener ejemplos de uso de estas API, consulte la entrada de blog Preview 4 (Versión preliminar 4).
Mejora del rendimiento en GZipStream para secuencias concatenadas
Una contribución de la comunidad mejoró el rendimiento de GZipStream al procesar flujos de datos concatenados de GZip. Anteriormente, cada nuevo segmento de flujo eliminaba y reasignaba el componente interno ZLibStreamHandle, lo que resultaba en asignaciones de memoria adicionales y recarga de inicialización. Con este cambio, el identificador ahora se restablece y reutiliza para reducir las asignaciones de memoria administradas y no administradas y mejorar el tiempo de ejecución. El mayor impacto (~35% más rápido) se ve al procesar un gran número de flujos de datos pequeños. Este cambio:
- Elimina la asignación repetida de ~64-80 bytes de memoria por secuencia concatenada, con ahorros de memoria no administrados adicionales.
- Reduce el tiempo de ejecución aproximadamente 400 ns por secuencia concatenada.
Administración de procesos de Windows
Iniciar procesos de Windows en un nuevo grupo de procesos
Para Windows, ahora puede usar ProcessStartInfo.CreateNewProcessGroup para iniciar un proceso en un grupo de procesos independiente. Esto le permite enviar señales aisladas a procesos secundarios que podrían quitar el elemento primario sin un control adecuado. El envío de señales es conveniente para evitar la terminación forzada.
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);
}
Mejoras de WebSocket
WebSocketStream
.NET 10 presenta , una nueva API diseñada para simplificar algunos de los escenarios WebSocketStreammás comunes (y anteriormente complicados)WebSocket en .NET.
Las API tradicionales WebSocket son de bajo nivel y requieren una reutilizable importante: controlar el almacenamiento en búfer y el marco, reconstruir mensajes, administrar la codificación y descodificación, y escribir contenedores personalizados para integrarse con secuencias, canales u otras abstracciones de transporte. Estas complejidades dificultan el uso de WebSockets como transporte, especialmente para aplicaciones con protocolos basados en streaming o texto, o controladores controlados por eventos.
WebSocketStream soluciona estos puntos débiles al proporcionar una Streamabstracción basada en webSocket. Esto permite una integración perfecta con las API existentes para leer, escribir y analizar datos, ya sean binarios o de texto, y reduce la necesidad de fontanería manual.
WebSocketStream habilita api de alto nivel y conocidas para patrones comunes de consumo y producción de WebSocket. Estas API reducen la fricción y facilitan la implementación de escenarios avanzados.
Patrones de uso comunes
Estos son algunos ejemplos de cómo WebSocketStream simplifica los flujos de trabajo típicos WebSocket :
Protocolo de texto de streaming (por ejemplo, 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`.
Protocolo binario de streaming (por ejemplo, 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.
Leer un único mensaje como una secuencia (por ejemplo, deserialización 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);
Escribir un único mensaje como una secuencia (por ejemplo, serialización binaria)
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().
Mejoras de TLS
TLS 1.3 para macOS (cliente)
.NET 10 agrega compatibilidad con TLS 1.3 del lado cliente en macOS mediante la integración de Network.framework de Apple en SslStream y HttpClient. Históricamente, macOS usaba transporte seguro que no admite TLS 1.3; la participación en Network.framework habilita TLS 1.3.
Ámbito y comportamiento
- Solo macOS, lado cliente en esta versión.
- Participar. Las aplicaciones existentes siguen usando la pila actual a menos que esté habilitada.
- Cuando está habilitada, es posible que las versiones anteriores de TLS (TLS 1.0 y 1.1) ya no estén disponibles a través de Network.framework.
Cómo habilitar
Use un modificador AppContext en el código:
// 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");
O bien, use una variable de entorno:
# 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
Notas
- TLS 1.3 se aplica a SslStream las API y basadas en ella (por ejemplo,HttpClient/HttpMessageHandler ).
- Los conjuntos de cifrado se controlan mediante macOS a través de Network.framework.
- El comportamiento de la secuencia subyacente puede diferir cuando Network.framework está habilitado (por ejemplo, almacenamiento en búfer, finalización de lectura y escritura, semántica de cancelación).
- La semántica puede diferir para las lecturas de bytes cero. Evite confiar en lecturas de longitud cero para detectar la disponibilidad de datos.
- Network.framework puede rechazar determinados nombres de host de nombres de dominio internacionalizados (IDN). Prefiere nombres de host ASCII/Punycode (A-label) o valida los nombres con restricciones macOS/Network.framework.
- Si la aplicación se basa en un comportamiento específico SslStream de casos perimetrales, valide en Network.framework.