Co nowego na platformie .NET 9

Dowiedz się więcej o nowych funkcjach na platformie .NET 9 i znajdź linki do dalszej dokumentacji.

Platforma .NET 9, następca platformy .NET 8, koncentruje się na aplikacjach i wydajności natywnych dla chmury. Będzie ona obsługiwana przez 18 miesięcy jako wersja standardowego wsparcia technicznego (STS). Możesz pobrać platformę .NET 9 tutaj.

Nowy dla platformy .NET 9 zespół inżynierów publikuje aktualizacje wersji zapoznawczej platformy .NET 9 w dyskusjach w witrynie GitHub. To doskonałe miejsce do zadawania pytań i przekazywania opinii na temat wydania.

Ten artykuł został zaktualizowany dla platformy .NET 9 (wersja zapoznawcza 2). W poniższych sekcjach opisano aktualizacje podstawowych bibliotek .NET na platformie .NET 9.

Środowisko uruchomieniowe platformy .NET

Serializacja

W System.Text.Jsonsystemie platforma .NET 9 ma nowe opcje serializacji kodu JSON i nowego pojedynczego pliku, który ułatwia serializację przy użyciu ustawień domyślnych sieci Web.

Opcje wcięcia

JsonSerializerOptions zawiera nowe właściwości, które umożliwiają dostosowanie znaku wcięcia i rozmiaru wcięcia zapisanego kodu JSON.

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    IndentCharacter = '\t',
    IndentSize = 2,
};

string json = JsonSerializer.Serialize(
    new { Value = 1 },
    options
    );
Console.WriteLine(json);
//{
//                "Value": 1
//}

Domyślne opcje sieci Web

Jeśli chcesz serializować z opcjami domyślnymi, które ASP.NET Core używa dla aplikacji internetowych, użyj nowego JsonSerializerOptions.Web pojedynczego pliku.

string webJson = JsonSerializer.Serialize(
    new { SomeValue = 42 },
    JsonSerializerOptions.Web // Defaults to camelCase naming policy.
    );
Console.WriteLine(webJson);
// {"someValue":42}

LINQ

Wprowadzono nowe metody CountByAggregateBy . Te metody umożliwiają agregowanie stanu według klucza bez konieczności przydzielania grup pośrednich za pośrednictwem metody GroupBy.

CountBy pozwala szybko obliczyć częstotliwość każdego klucza. W poniższym przykładzie znajduje się słowo, które występuje najczęściej w ciągu tekstowym.

string sourceText = """
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Sed non risus. Suspendisse lectus tortor, dignissim sit amet, 
    adipiscing nec, ultricies sed, dolor. Cras elementum ultrices amet diam.
""";

// Find the most frequent word in the text.
KeyValuePair<string, int> mostFrequentWord = sourceText
    .Split(new char[] { ' ', '.', ',' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(word => word.ToLowerInvariant())
    .CountBy(word => word)
    .MaxBy(pair => pair.Value);

Console.WriteLine(mostFrequentWord.Key); // amet

AggregateBy umożliwia implementowanie bardziej ogólnych przepływów pracy. W poniższym przykładzie pokazano, jak można obliczyć wyniki skojarzone z danym kluczem.

(string id, int score)[] data =
    [
        ("0", 42),
        ("1", 5),
        ("2", 4),
        ("1", 10),
        ("0", 25),
    ];

var aggregatedData =
    data.AggregateBy(
        keySelector: entry => entry.id,
        seed: 0,
        (totalScore, curr) => totalScore + curr.score
        );

foreach (var item in aggregatedData)
{
    Console.WriteLine(item);
}
//(0, 67)
//(1, 15)
//(2, 4)

Index<TSource>(IEnumerable<TSource>) umożliwia szybkie wyodrębnianie niejawnego indeksu wyliczalnego. Teraz możesz napisać kod, taki jak poniższy fragment kodu, aby automatycznie indeksować elementy w kolekcji.

IEnumerable<string> lines2 = File.ReadAllLines("output.txt");
foreach ((int index, string line) in lines2.Index())
{
    Console.WriteLine($"Line number: {index + 1}, Line: {line}");
}

Kolekcje

Typ PriorityQueue<TElement,TPriority> kolekcji w System.Collections.Generic przestrzeni nazw zawiera nową Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) metodę, której można użyć do zaktualizowania priorytetu elementu w kolejce.

PriorityQueue.Remove(), metoda

Platforma .NET 6 wprowadziła PriorityQueue<TElement,TPriority> kolekcję, która zapewnia prostą i szybką implementację stert tablicy. Jednym z problemów z stertami tablicy jest to, że nie obsługują aktualizacji priorytetów, co sprawia, że są one zbyt uciążliwe do użycia w algorytmach, takich jak odmiany algorytmu Dijkstra.

Chociaż nie można zaimplementować wydajnych aktualizacji priorytetów $O(\log n)$ w istniejącej kolekcji, nowa PriorityQueue<TElement,TPriority>.Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) metoda umożliwia emulowanie aktualizacji priorytetów (choć w czasie $O(n)$):

public static void UpdatePriority<TElement, TPriority>(
    this PriorityQueue<TElement, TPriority> queue,
    TElement element,
    TPriority priority
    )
{
    // Scan the heap for entries matching the current element.
    queue.Remove(element, out _, out _);
    // Re-insert the entry with the new priority.
    queue.Enqueue(element, priority);
}

Ta metoda odblokowuje użytkowników, którzy chcą implementować algorytmy grafu w kontekstach, w których wydajność asymptotyczna nie jest blokowaniem. (Takie konteksty obejmują edukację i tworzenie prototypów). Na przykład poniżej przedstawiono implementację algorytmu toy Dijkstra, który używa nowego interfejsu API.

Kryptografia

W przypadku kryptografii platforma .NET 9 dodaje nową jednostrzeloną metodę skrótu CryptographicOperations dla typu. Dodaje również nowe klasy korzystające z algorytmu KMAC.

CryptographicOperations.HashData(), metoda

Platforma .NET zawiera kilka statycznych implementacji funkcji skrótu i powiązanych funkcji skrótu. Te interfejsy API obejmują SHA256.HashData i HMACSHA256.HashData. Interfejsy API jednorazowe są preferowane do użycia, ponieważ mogą zapewnić najlepszą możliwą wydajność i zmniejszyć lub wyeliminować alokacje.

Jeśli deweloper chce udostępnić interfejs API, który obsługuje wyznaczanie wartości skrótu, w którym obiekt wywołujący definiuje algorytm wyznaczania wartości skrótu, zwykle odbywa się to przez akceptowanie argumentu HashAlgorithmName . Jednak użycie tego wzorca z interfejsami API jednoskrętowymi wymagałoby przełączenia się na wszystkie możliwe HashAlgorithmName , a następnie przy użyciu odpowiedniej metody. Aby rozwiązać ten problem, platforma CryptographicOperations.HashData .NET 9 wprowadza interfejs API. Ten interfejs API umożliwia utworzenie skrótu lub HMAC dla danych wejściowych jako jednostrzelenia, w którym używany algorytm jest określany przez element HashAlgorithmName.

static void HashAndProcessData(HashAlgorithmName hashAlgorithmName, byte[] data)
{
    byte[] hash = CryptographicOperations.HashData(hashAlgorithmName, data);
    ProcessHash(hash);
}

Algorytm KMAC

Platforma .NET 9 udostępnia algorytm KMAC określony przez NIST SP-800-185. KeCCAK Message Authentication Code (KMAC) to funkcja pseudorandom i funkcja skrótu klucza oparta na keCCAK.

Następujące nowe klasy używają algorytmu KMAC. Użyj wystąpień, aby zebrać dane w celu utworzenia komputera MAC lub użyć metody statycznej HashData dla jednego rzutu danych wejściowych.

Funkcja KMAC jest dostępna w systemie Linux z programem OpenSSL 3.0 lub nowszym oraz w systemie Windows 11 Build 26016 lub nowszym. Możesz użyć właściwości statycznej IsSupported , aby określić, czy platforma obsługuje żądany algorytm.

if (Kmac128.IsSupported)
{
    byte[] key = GetKmacKey();
    byte[] input = GetInputToMac();
    byte[] mac = Kmac128.HashData(key, input, outputLength: 32);
}
else
{
    // Handle scenario where KMAC isn't available.
}

Odbicie

W wersjach .NET Core i .NET 5-8 obsługa kompilowania zestawu i emitowania metadanych odbicia dla dynamicznie utworzonych typów była ograniczona do możliwego AssemblyBuilderdo uruchomienia . Brak obsługi zapisywania zestawu był często blokowaniem dla klientów migrujących z programu .NET Framework do platformy .NET. Platforma .NET 9 dodaje publiczne interfejsy API w celu AssemblyBuilder zapisania emitowanego zestawu.

Nowa, utrwalone AssemblyBuilder implementacja jest niezależna od środowiska uruchomieniowego i platformy. Aby utworzyć utrwalone AssemblyBuilder wystąpienie, użyj nowego AssemblyBuilder.DefinePersistedAssembly interfejsu API. Istniejący AssemblyBuilder.DefineDynamicAssembly interfejs API akceptuje nazwę zestawu i opcjonalne atrybuty niestandardowe. Aby użyć nowego interfejsu API, przekaż podstawowy zestaw , System.Private.CoreLibktóry jest używany do odwoływania się do podstawowych typów środowiska uruchomieniowego. Nie ma opcji dla elementu AssemblyBuilderAccess. Na razie utrwalone AssemblyBuilder wdrożenie obsługuje tylko zapisywanie, a nie uruchamianie. Po utworzeniu wystąpienia utrwalonego AssemblyBuilderkolejne kroki definiowania modułu, typu, metody lub wyliczenia, zapisu il i wszystkich innych użycia pozostają niezmienione. Oznacza to, że do zapisywania zestawu można użyć istniejącego System.Reflection.Emit kodu zgodnie z rzeczywistym użyciem. Poniższy kod przedstawia przykład.

public void CreateAndSaveAssembly(string assemblyPath)
{
    AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(
        new AssemblyName("MyAssembly"),
        typeof(object).Assembly
        );
    TypeBuilder tb = ab.DefineDynamicModule("MyModule")
        .DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);

    MethodBuilder mb = tb.DefineMethod(
        "SumMethod",
        MethodAttributes.Public | MethodAttributes.Static,
        typeof(int), [typeof(int), typeof(int)]
        );
    ILGenerator il = mb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Ret);

    tb.CreateType();
    ab.Save(assemblyPath); // or could save to a Stream
}

public void UseAssembly(string assemblyPath)
{
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    Type type = assembly.GetType("MyType");
    MethodInfo method = type.GetMethod("SumMethod");
    Console.WriteLine(method.Invoke(null, [5, 10]));
}

Wydajność

Platforma .NET 9 zawiera ulepszenia 64-bitowego kompilatora JIT mającego na celu poprawę wydajności aplikacji. Te ulepszenia kompilatora obejmują:

Wektoryzacja arm64 to kolejna nowa funkcja środowiska uruchomieniowego.

Optymalizacje pętli

Ulepszanie generowania kodu dla pętli jest priorytetem dla platformy .NET 9, a kompilator 64-bitowy oferuje nową optymalizację o nazwie rozszerzanie zmiennej indukcji (IV).

Iv to zmienna, której wartość zmienia się jako iterowanie pętli zawierającej. W następującej for pętli i jest iv: for (int i = 0; i < 10; i++). Jeśli kompilator może przeanalizować, jak wartość IV ewoluuje w iteracji pętli, może wygenerować bardziej wydajny kod dla powiązanych wyrażeń.

Rozważmy następujący przykład iterujący przez tablicę:

static int Sum(int[] arr)
{
    int sum = 0;
    for (int i = 0; i < arr.Length; i++)
    {
        sum += arr[i];
    }

    return sum;
}

Zmienna indeksu, i, ma rozmiar 4 bajty. Na poziomie zestawu rejestry 64-bitowe są zwykle używane do przechowywania indeksów tablicy na x64, a w poprzednich wersjach platformy .NET kompilator wygenerował kod, który zera został rozszerzony i do 8 bajtów dla dostępu do tablicy, ale nadal traktuje i się jako 4-bajtową liczbę całkowitą w innym miejscu. Jednak rozszerzenie i do 8 bajtów wymaga dodatkowej instrukcji na x64. Dzięki rozszerzaniu iv kompilator JIT 64-bitowy rozszerza i się teraz do 8 bajtów w całej pętli, pomijając rozszerzenie zero. Pętla na tablicach jest bardzo powszechna, a korzyści wynikające z tego usuwania instrukcji szybko się sumuje.

Ulepszenia tworzenia pod kątem natywnej funkcji AOT

Jeden z . Celem net dla 64-bitowego kompilatora JIT jest usunięcie jak największej liczby ograniczeń, które blokują tworzenie wbudowanej metody. Platforma .NET 9 umożliwia podkreślenie dostępu do statycznych wątków lokalnych w systemach Windows x64, Linux x64 i Linux Arm64.

W przypadku static składowych klasy istnieje dokładnie jedno wystąpienie elementu członkowskiego we wszystkich wystąpieniach klasy, które "współużytkuje" składowy. Jeśli wartość static elementu członkowskiego jest unikatowa dla każdego wątku, dzięki czemu wartość wątku lokalnego może poprawić wydajność, ponieważ eliminuje potrzebę elementu pierwotnego współbieżności, aby bezpiecznie uzyskać dostęp do static elementu członkowskiego z jego zawartego wątku.

Wcześniej dostęp do statycznych wątków lokalnych w natywnych programach skompilowanych za pomocą funkcji AOT wymagał kompilatora JIT 64-bitowego w celu emitowania wywołania do środowiska uruchomieniowego w celu uzyskania podstawowego adresu magazynu wątkowego lokalnego. Teraz kompilator może w tekście tworzyć te wywołania, co skutkuje znacznie mniejszą liczbą instrukcji dotyczących uzyskiwania dostępu do tych danych.

Ulepszenia PGO: kontrole typów i rzutowanie

Domyślnie platforma .NET 8 włączyła optymalizację dynamiczną z przewodnikiem z profilem (PGO ). Platforma NET 9 rozszerza implementację PGO kompilatora JIT 64-bitowego, aby profilować więcej wzorców kodu. Gdy kompilacja warstwowa jest włączona, 64-bitowy kompilator JIT wstawia już instrumentację do programu w celu profilowania jego zachowania. Podczas ponownego kompilowania przy użyciu optymalizacji kompilator korzysta z profilu utworzonego w czasie wykonywania, aby podejmować decyzje specyficzne dla bieżącego uruchomienia programu. Na platformie .NET 9 64-bitowy kompilator JIT używa danych PGO w celu zwiększenia wydajności kontroli typów.

Określenie typu obiektu wymaga wywołania do środowiska uruchomieniowego, które wiąże się z karą za wydajność. Gdy typ obiektu należy sprawdzić, kompilator 64-bitowy JIT emituje to wywołanie ze względu na poprawność (kompilatory zwykle nie mogą wykluczyć żadnych możliwości, nawet jeśli wydają się nieprawdopodobne). Jeśli jednak dane PGO sugerują, że obiekt może być określonym typem, 64-bitowy kompilator JIT emituje teraz szybką ścieżkę, która tanio sprawdza ten typ i wraca do powolnej ścieżki wywoływania środowiska uruchomieniowego tylko w razie potrzeby.

Wektoryzacja arm64 w bibliotekach platformy .NET

Nowa EncodeToUtf8 implementacja korzysta z możliwości 64-bitowego kompilatora JIT do emitowania instrukcji dotyczących obciążenia/przechowywania wielu rejestrów w usłudze Arm64. To zachowanie umożliwia programom przetwarzanie większych fragmentów danych z mniejszą liczbą instrukcji. Aplikacje platformy .NET w różnych domenach powinny widzieć ulepszenia przepływności na sprzęcie Arm64 obsługującym te funkcje. Niektóre testy porównawcze skróciły czas wykonywania o ponad połowę.

Zestaw SDK platformy .NET

Testowanie jednostek

W tej sekcji opisano aktualizacje testowania jednostkowego na platformie .NET 9: równoległe uruchamianie testów oraz dane wyjściowe testu rejestratora terminalu.

Równoległe uruchamianie testów

Na platformie .NET 9 dotnet test jest w pełni zintegrowany z programem MSBuild. Ponieważ program MSBuild obsługuje równoległe tworzenie, można uruchamiać testy dla tego samego projektu w różnych platformach docelowych równolegle. Domyślnie program MSBuild ogranicza liczbę procesów równoległych do liczby procesorów na komputerze. Możesz również ustawić własny limit przy użyciu przełącznika -maxcpucount . Jeśli chcesz zrezygnować z równoległości, ustaw TestTfmsInParallel właściwość MSBuild na falsewartość .

Wyświetlanie testu rejestratora terminalu

Raportowanie wyników testów dla programu dotnet test jest teraz obsługiwane bezpośrednio w rejestratorze terminalu programu MSBuild. Uzyskasz bardziej funkcjonalne raportowanie testów zarówno podczas uruchamiania testów (wyświetla działającą nazwę testu), jak i po zakończeniu testów (wszelkie błędy testów są renderowane w lepszy sposób).

Aby uzyskać więcej informacji na temat rejestratora terminalu, zobacz dotnet build options (Opcje kompilacji dotnet).

Roll-forward narzędzia .NET

Narzędzia platformy .NET to aplikacje zależne od platformy, które można instalować globalnie lub lokalnie, a następnie uruchamiać przy użyciu zestawu .NET SDK i zainstalowanych środowisk uruchomieniowych platformy .NET. Te narzędzia, takie jak wszystkie aplikacje platformy .NET, dotyczą określonej wersji głównej platformy .NET. Domyślnie aplikacje nie są uruchamiane w nowszych wersjach platformy .NET. Autorzy narzędzi mogli wyrazić zgodę na uruchamianie swoich narzędzi w nowszych wersjach środowiska uruchomieniowego platformy .NET przez ustawienie RollForward właściwości MSBuild. Jednak nie wszystkie narzędzia to robią.

Nowa opcja dotnet tool install umożliwia użytkownikom decydowanie o tym, jak powinny być uruchamiane narzędzia .NET. Podczas instalowania narzędzia za pomocą dotnet tool installprogramu lub podczas uruchamiania narzędzia za pomocą dotnet tool run <toolname>polecenia można określić nową flagę o nazwie --allow-roll-forward. Ta opcja umożliwia skonfigurowanie narzędzia z trybem Majorwycofywania. Ten tryb umożliwia uruchamianie narzędzia w nowszej wersji głównej platformy .NET, jeśli zgodna wersja platformy .NET jest niedostępna. Ta funkcja pomaga wczesnym adoptatorom używać narzędzi platformy .NET bez konieczności zmiany kodu przez autorów narzędzi.

Zobacz też