Поделиться через


Новые возможности библиотек .NET для .NET 10

В этой статье описываются новые возможности библиотек .NET для .NET 10. Оно было обновлено для предварительной версии 6.

Криптография

Поиск сертификатов по отпечаткам, отличным от SHA-1

Поиск сертификатов однозначно по отпечатку является довольно распространенной операцией, но X509Certificate2Collection.Find(X509FindType, Object, Boolean) метод (для FindByThumbprint режима) ищет только значение отпечатка SHA-1.

Существует некоторый риск использования Find метода для поиска SHA-2-256 (SHA256) и SHA-3-256 отпечатков, так как эти хэш-алгоритмы имеют ту же длину.

Вместо этого .NET 10 представляет новый метод , который принимает имя хэш-алгоритма для сопоставления.

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();

Поиск данных в кодировке PEM в ASCII/UTF-8

Кодировка PEM (первоначально Privacy Enhanced Mail, но в настоящее время широко используется за пределами электронной почты) определена для "текста", что означает, что PemEncoding класс был разработан для запуска String и ReadOnlySpan<char>. Однако обычно (особенно в Linux) есть сертификат, написанный в файле, использующий кодировку ASCII (string). Исторически это означает, что необходимо открыть файл и преобразовать байты в chars (или строку), прежде чем можно было использовать PemEncoding.

Новый PemEncoding.FindUtf8(ReadOnlySpan<Byte>) метод использует преимущество того факта, что PEM определен только для 7-разрядных символов ASCII, и что 7-разрядный ASCII имеет идеальное перекрытие с однобайтовым значением UTF-8. Вызвав этот новый метод, можно пропустить преобразование UTF-8/ASCII в char и прочитать файл напрямую.

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]);

Алгоритм шифрования для экспорта PKCS#12/PFX

Новые методы на ExportPkcs12 и X509Certificate позволяют вызывающим пользователям выбирать, какие алгоритмы шифрования и хэширования используются для создания выходных данных.

  • Pkcs12ExportPbeParameters.Pkcs12TripleDesSha1 указывает, что стандарт Windows XP-эры де-факто является стандартным. Он создает выходные данные, поддерживаемые почти каждой библиотекой и платформой, которая поддерживает чтение PKCS#12/PFX, выбрав старый алгоритм шифрования.
  • Pkcs12ExportPbeParameters.Pbes2Aes256Sha256 указывает, что AES следует использовать вместо 3DES (и SHA-2-256 вместо SHA-1), но выходные данные могут не пониматься всеми средствами чтения (например, Windows XP).

Если вы хотите еще больше управления, можно использовать перегрузку , которая принимает значение PbeParameters.

Постквантовая криптография (PQC)

.NET 10 включает поддержку трех новых асимметричных алгоритмов: ML-KEM (FIPS 203), ML-DSA (FIPS 204) и SLH-DSA (FIPS 205). Новые типы:

  • System.Security.Cryptography.MLKem
  • System.Security.Cryptography.MLDsa
  • System.Security.Cryptography.SlhDsa

Поскольку он добавляет мало преимуществ, эти новые типы не являются производными от AsymmetricAlgorithm. AsymmetricAlgorithm Вместо подхода к созданию объекта, а затем импорту ключа в него или созданию нового ключа все типы используют статические методы для создания или импорта ключа:

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);
    }
}

Вместо того, чтобы задавать свойства объекта и материализовать ключ, генерация ключей на этих новых типах учитывает все необходимые варианты.

using (MLKem key = MLKem.GenerateKey(MLKemAlgorithm.MLKem768))
{
    string publicKeyPem = key.ExportSubjectPublicKeyInfoPem();
    ...
}

Эти алгоритмы продолжают использовать шаблон статического IsSupported свойства, чтобы указать, поддерживается ли алгоритм в текущей системе.

.NET 10 включает API шифрования Windows: поддержка следующего поколения (CNG) для шифрования Post-Quantum (PQC), что делает эти алгоритмы доступными в системах Windows с поддержкой PQC. Рассмотрим пример.

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);
}

Алгоритмы PQC доступны в системах, где системные криптографические библиотеки — OpenSSL 3.5 (или более поздней версии) или Windows CNG с поддержкой PQC. Кроме того, новые классы помечены как [Experimental] в разделе диагностики SYSLIB5006 до завершения разработки.

Глобализация и дата/время

Новые перегрузки методов в ISOWeek для типа DateOnly

Класс ISOWeek изначально был разработан для работы исключительно с DateTime, так как он был введен до того, как тип DateOnly существовал. Теперь, когда DateOnly доступен, имеет смысл, чтобы ISOWeek также поддерживал его. Следующие перегрузки являются новыми:

Числовое упорядочение для сравнения строк

Сравнение числовых строк — это высоко запрошенная функция для сравнения строк числовым образом, а не лексографически. Например, 2 меньше 10, поэтому "2" следует отображаться раньше "10" при упорядочении числовых значений. Аналогично, "2" и "02" численно равны. С новым NumericOrdering вариантом теперь можно выполнить следующие типы сравнений:

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

Этот параметр недействителен для следующих строковых операций на основе индекса: IndexOf, , LastIndexOfStartsWith, EndsWithи IsPrefixIsSuffix.

Новая TimeSpan.FromMilliseconds перегрузка с одним параметром

Метод TimeSpan.FromMilliseconds(Int64, Int64) был введен ранее без добавления перегрузки, которая принимает один параметр.

Хотя это работает, так как второй параметр является необязательным, он вызывает ошибку компиляции при использовании в выражении LINQ, например:

Expression<Action> a = () => TimeSpan.FromMilliseconds(1000);

Проблема возникает, так как выражения LINQ не могут обрабатывать необязательные параметры. Для решения этой проблемы .NET 10 представляет новую перегрузку , которая принимает один параметр. Он также изменяет существующий метод , чтобы сделать второй параметр обязательным.

Строки

API нормализации строк для работы с диапазоном символов

Нормализация строк Юникода поддерживается в течение длительного времени, но существующие API работали только с типом строки. Это означает, что вызывающие стороны с данными, хранящимися в разных формах, таких как массивы символов или интервалы, должны выделить новую строку для использования этих API-интерфейсов. Кроме того, API, возвращающие нормализованную строку, всегда выделяют новую строку для представления нормализованных выходных данных.

.NET 10 представляет новые API, которые работают с диапазонами символов, которые расширяют нормализацию за пределами строковых типов и помогают избежать ненужных выделений:

Коллекции

Дополнительные TryAdd и TryGetValue перегрузки для OrderedDictionary<TKey, TValue>

OrderedDictionary<TKey,TValue> предоставляет TryAdd и TryGetValue для добавления и извлечения, как и для любой другой IDictionary<TKey, TValue> реализации. Однако существуют сценарии, в которых может потребоваться выполнить больше операций, поэтому добавляются новые перегрузки, которые возвращают индекс к записи.

Затем этот индекс можно использовать с GetAt и SetAt для быстрого доступа к записи. Пример использования новой TryAdd перегрузки заключается в добавлении или обновлении пары "ключ-значение" в упорядоченном словаре:

// 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);
}

Этот новый API уже используется JsonObject и повышает производительность обновления свойств на 10–20%.

Сериализация

Разрешить указание ReferenceHandler в JsonSourceGenerationOptions

При использовании генераторов источников для сериализации JSON созданный контекст вызывается при сериализации или десериализации циклов. Теперь вы можете настроить это поведение, указав ReferenceHandler в JsonSourceGenerationOptionsAttribute. Ниже приведен пример использования 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!;
}

Параметр запретить повторяющиеся свойства JSON

Спецификация JSON не указывает, как обрабатывать повторяющиеся свойства при десериализации полезных данных JSON. Это может привести к непредвиденным результатам и уязвимостям системы безопасности. В .NET 10 представлен JsonSerializerOptions.AllowDuplicateProperties параметр, который запрещает повторение свойств JSON:

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);

Дубликаты обнаруживаются путем проверки того, назначено ли значение несколько раз во время десериализации, поэтому это функционирует как следует с другими параметрами, такими как чувствительность к регистру и политика именования.

Строгие параметры сериализации JSON

Сериализатор JSON принимает множество параметров для настройки сериализации и десериализации, но значения по умолчанию могут быть слишком расслаблены для некоторых приложений. .NET 10 добавляет новую JsonSerializerOptions.Strict предустановку, которая соответствует рекомендациям, включая следующие варианты:

Эти параметры совместимы с JsonSerializerOptions.Default — объект, сериализованный с помощью JsonSerializerOptions.Default, можно десериализовать с помощью JsonSerializerOptions.Strict.

Дополнительные сведения о сериализации JSON см. в обзоре System.Text.Json.

System.Numerics

Дополнительные методы преобразования матрицы с левой рукой

.NET 10 добавляет оставшиеся API для создания левоориентированных матриц преобразования для билбордов и ограниченных билборд-матриц. Эти методы можно использовать аналогично их существующим правосторонним аналогам, например CreateBillboard(Vector3, Vector3, Vector3, Vector3), когда вместо этого используется левосторонняя система координат.

Усовершенствования Tensor

Теперь интерфейс System.Numerics.Tensors включает негенерический интерфейс IReadOnlyTensor для операций, таких как доступ к Lengths и Strides. Операции среза больше не копируют данные, что повышает производительность. Кроме того, вы можете получить доступ к данным без использования обобщений, упаковывая их в object, если скорость работы не является критически важной.

Проверка параметров

Новый конструктор AOT-safe для ValidationContext

Класс ValidationContext, используемый во время проверки параметров, включает новую перегрузку конструктора, которая однозначно принимает параметр displayName.

ValidationContext(Object, String, IServiceProvider, IDictionary<Object,Object>)

Отображаемое имя гарантирует безопасность AOT и позволяет использовать его в собственных сборках без предупреждений.

Диагностика

Поддержка URL-адресов схемы телеметрии в ActivitySource и Meter

ActivitySource и Meter теперь поддерживает указание URL-адреса схемы телеметрии во время построения, которая соответствует спецификациям OpenTelemetry. Схема телеметрии обеспечивает согласованность и совместимость данных трассировки и метрик. Кроме того, в .NET 10 представлено ActivitySourceOptions, что упрощает создание экземпляров ActivitySource с несколькими параметрами конфигурации (включая URL схемы телеметрии).

Новые API:

Класс Activity обеспечивает распределенную трассировку, отслеживая ход операций между службами и компонентами. .NET поддерживает сериализацию этих данных трассировки вне процесса с использованием поставщика событий Microsoft-Diagnostics-DiagnosticSource. Объект Activity может включать дополнительные метаданные, такие как ActivityLink и ActivityEvent. .NET 10 добавляет поддержку сериализации этих ссылок и событий, поэтому данные трассировки вне proc теперь включают эти сведения. Рассмотрим пример.

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)]"

Поддержка трассировки с лимитированием частоты

Если данные распределенной трассировки сериализуются вне процесса через Microsoft-Diagnostics-DiagnosticSource поставщика источника событий, все записанные активности могут быть переданы или на основе соотношения трассировки может быть применена выборка.

Новый параметр выборки с именем "Ограничение скорости выборки" ограничивает количество сериализованных корневых действий в секунду. Это помогает более точно управлять объемом данных.

Агрегаторы данных трассировки вне proc могут включить и настроить эту выборку, указав параметр в FilterAndPayloadSpecs. Например, следующий параметр ограничивает сериализацию до 100 корневых действий в секунду во всех ActivitySource экземплярах:

[AS]*/-ParentRateLimitingSampler(100)

ZIP-файлы

Улучшения производительности и памяти ZipArchive

.NET 10 повышает производительность и использование ZipArchiveпамяти.

Во-первых, способ записи записей в ZipArchive, находясь в режиме Update, был оптимизирован. Ранее все ZipArchiveEntry экземпляры загружались в память и перезаписывались, что могло привести к высокому использованию памяти и узким местам в производительности. Оптимизация сокращает использование памяти и повышает производительность, избегая загрузки всех записей в память.

Во-вторых, извлечение ZipArchive записей теперь параллелизировано, а внутренние структуры данных оптимизированы для улучшения использования памяти. Эти улучшения устраняют проблемы, связанные с узкими местами производительности и большим объемом памяти, что делает ZipArchive более эффективным и быстрым, особенно при работе с большими архивами.

Новые асинхронные API ZIP

.NET 10 представляет новые асинхронные API, которые упрощают выполнение неблокирующих операций при чтении или записи в ZIP-файлы. Эта функция была очень запрошена сообществом.

Новые async методы доступны для извлечения, создания и обновления ZIP-архивов. Эти методы позволяют разработчикам эффективно обрабатывать большие файлы и улучшать скорость реагирования приложений, особенно в сценариях, связанных с операциями ввода-вывода. К этим методам относятся:

Примеры использования этих API см. в записи блога preview 4.

Повышение производительности в GZipStream для объединенных потоков

Вклад сообщества улучшил производительность GZipStream при обработке объединенных потоков данных GZip. Ранее каждый новый сегмент потока удалял и перераспределял внутренний ZLibStreamHandle, что приводило к дополнительным выделениям памяти и накладным расходам на инициализацию. При этом изменении дескриптор теперь сбрасывается и используется повторно, чтобы уменьшить объем выделяемой управляемой и неуправляемой памяти и сократить время выполнения. Наибольший эффект (~35% быстрее) существенно проявляется при обработке большого количества небольших потоков данных. Это изменение:

  • Устраняет повторное выделение около 64–80 байт памяти на каждый объединенный поток, дополнительно экономя неуправляемую память.
  • Сокращает время выполнения примерно на 400 ns на объединенный поток.