Delen via


Nieuw in .NET 9

Meer informatie over de nieuwe functies in .NET 9 en koppelingen naar verdere documentatie.

.NET 9, de opvolger van .NET 8, heeft een speciale focus op cloudeigen apps en prestaties. Deze wordt gedurende 18 maanden ondersteund als een STANDAARD-term support (STS). U kunt .NET 9 hier downloaden.

Nieuw voor .NET 9, het technische team plaatst .NET 9 preview-updates op GitHub-discussies. Dat is een geweldige plek om vragen te stellen en feedback te geven over de release.

Dit artikel is bijgewerkt voor .NET 9 Preview 2. In de volgende secties worden de updates voor de .NET-kernbibliotheken in .NET 9 beschreven.

.NET-runtime

Serialization

In System.Text.Json.NET 9 zijn er nieuwe opties voor het serialiseren van JSON en een nieuwe singleton waarmee u eenvoudiger kunt serialiseren met behulp van webstandaarden.

Opties voor inspringing

JsonSerializerOptions bevat nieuwe eigenschappen waarmee u het inspringingsteken en de inspringingsgrootte van geschreven JSON kunt aanpassen.

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

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

Standaardwebopties

Als u wilt serialiseren met de standaardopties die ASP.NET Core gebruikt voor web-apps, gebruikt u de nieuwe JsonSerializerOptions.Web singleton.

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

LINQ

Nieuwe methoden CountBy en AggregateBy zijn geïntroduceerd. Deze methoden maken het mogelijk om de status per sleutel te aggregeren zonder tussenliggende groeperingen toe te wijzen via GroupBy.

CountBy hiermee kunt u snel de frequentie van elke sleutel berekenen. In het volgende voorbeeld wordt het woord gevonden dat het vaakst voorkomt in een tekenreeks.

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 hiermee kunt u meer algemene werkstromen implementeren. In het volgende voorbeeld ziet u hoe u scores kunt berekenen die zijn gekoppeld aan een bepaalde sleutel.

(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>) maakt het mogelijk om snel de impliciete index van een opsomming te extraheren. U kunt nu code schrijven, zoals het volgende codefragment, om items in een verzameling automatisch te indexeren.

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

Verzamelingen

Het PriorityQueue<TElement,TPriority> verzamelingstype in de System.Collections.Generic naamruimte bevat een nieuwe Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) methode die u kunt gebruiken om de prioriteit van een item in de wachtrij bij te werken.

Methode PriorityQueue.Remove()

.NET 6 heeft de PriorityQueue<TElement,TPriority> verzameling geïntroduceerd, die een eenvoudige en snelle matrix-heap-implementatie biedt. Een probleem met matrix-heaps in het algemeen is dat ze geen ondersteuning bieden voor prioriteitsupdates, waardoor ze verboden zijn voor gebruik in algoritmen, zoals variaties van het algoritme van Azure.

Hoewel het niet mogelijk is om efficiënte $O(\log n)$ prioriteitsupdates in de bestaande verzameling te implementeren, maakt de nieuwe PriorityQueue<TElement,TPriority>.Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) methode het mogelijk om prioriteitsupdates te emuleren (zij het op $O(n)$ tijd):

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

Met deze methode worden gebruikers gedeblokkeerd die grafiekalgoritmen willen implementeren in contexten waarbij asymptotische prestaties geen blokkering zijn. (Dergelijke contexten omvatten onderwijs en prototypen.) Hier volgt bijvoorbeeld een speelgoedimplementatie van het algoritme van Microsoft dat gebruikmaakt van de nieuwe API.

Cryptografie

Voor cryptografie voegt .NET 9 een nieuwe hash-methode met één shot toe aan het CryptographicOperations type. Er worden ook nieuwe klassen toegevoegd die gebruikmaken van het KMAC-algoritme.

Methode CryptographicOperations.HashData()

.NET bevat verschillende statische 'one-shot' -implementaties van hash-functies en gerelateerde functies. Deze API's omvatten SHA256.HashData en HMACSHA256.HashData. Api's met één shot hebben de voorkeur om te gebruiken, omdat ze de best mogelijke prestaties kunnen bieden en toewijzingen kunnen verminderen of elimineren.

Als een ontwikkelaar een API wil bieden die ondersteuning biedt voor hashing waarbij de aanroeper definieert welk hash-algoritme moet worden gebruikt, wordt dit meestal gedaan door een HashAlgorithmName argument te accepteren. Het gebruik van dat patroon met API's met één shot vereist echter elke mogelijke HashAlgorithmName overschakeling en vervolgens met behulp van de juiste methode. Om dit probleem op te lossen, introduceert .NET 9 de CryptographicOperations.HashData API. Met deze API kunt u een hash of HMAC produceren via een invoer als één shot waarbij het gebruikte algoritme wordt bepaald door een HashAlgorithmName.

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

KMAC-algoritme

.NET 9 biedt het KMAC-algoritme zoals opgegeven door NIST SP-800-185. KECCAK Message Authentication Code (KMAC) is een pseudorandom-functie en keyed hash-functie op basis van KECCAK.

In de volgende nieuwe klassen wordt het KMAC-algoritme gebruikt. Gebruik exemplaren om gegevens te verzamelen om een MAC te produceren of gebruik de statische HashData methode voor een eenmalige opname via één invoer.

KMAC is beschikbaar in Linux met OpenSSL 3.0 of hoger en op Windows 11 Build 26016 of hoger. U kunt de statische IsSupported eigenschap gebruiken om te bepalen of het platform het gewenste algoritme ondersteunt.

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.
}

Reflectie

In .NET Core-versies en .NET 5-8 was de ondersteuning voor het bouwen van een assembly en het verzenden van weerspiegelingsmetagegevens voor dynamisch gemaakte typen beperkt tot een uitvoerbaar AssemblyBuilder. Het gebrek aan ondersteuning voor het opslaan van een assembly was vaak een obstakel voor klanten die migreren van .NET Framework naar .NET. .NET 9 voegt openbare API's toe om een verzonden assembly op te AssemblyBuilder slaan.

De nieuwe, persistente AssemblyBuilder implementatie is runtime en platformonafhankelijk. Gebruik de nieuwe AssemblyBuilder.DefinePersistedAssembly API om een persistent exemplaar AssemblyBuilder te maken. De bestaande AssemblyBuilder.DefineDynamicAssembly API accepteert de assemblynaam en optionele aangepaste kenmerken. Als u de nieuwe API wilt gebruiken, geeft u de kernassembly door, System.Private.CoreLibdie wordt gebruikt voor het verwijzen naar basisruntimetypen. Er is geen optie voor AssemblyBuilderAccess. En voorlopig ondersteunt de persistente AssemblyBuilder implementatie alleen opslaan, niet worden uitgevoerd. Nadat u een exemplaar van het persistente exemplaar hebt gemaakt AssemblyBuilder, blijven de volgende stappen voor het definiëren van een module, type, methode of enum, het schrijven van IL en alle andere gebruiksrechten ongewijzigd. Dit betekent dat u bestaande System.Reflection.Emit code als zodanig kunt gebruiken om de assembly op te slaan. De volgende code laat een voorbeeld zien.

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

Prestaties

.NET 9 bevat verbeteringen in de 64-bits JIT-compiler die zijn gericht op het verbeteren van de app-prestaties. Deze verbeteringen in de compiler zijn onder andere:

Arm64-vectorisatie is een andere nieuwe functie van de runtime.

Lusoptimalisaties

Het verbeteren van het genereren van code voor lussen is een prioriteit voor .NET 9 en de 64-bits compiler bevat een nieuwe optimalisatie genaamd inductievariabele (IV) widening.

Een IV is een variabele waarvan de waarde verandert terwijl de lussen herhalen. In de volgende for lus i is een IV: for (int i = 0; i < 10; i++). Als de compiler kan analyseren hoe de waarde van een IV zich ontwikkelt ten opzichte van de iteraties van de lus, kan deze meer performante code produceren voor gerelateerde expressies.

Bekijk het volgende voorbeeld dat door een matrix wordt herhaald:

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

    return sum;
}

De indexvariabele is i4 bytes groot. Op assemblyniveau worden 64-bits registers meestal gebruikt voor het opslaan van matrixindexen op x64 en in eerdere .NET-versies, de compiler gegenereerde code die nul is uitgebreid i tot 8 bytes voor de matrixtoegang, maar verder wordt behandeld i als een geheel getal van 4 byte elders. Voor het uitbreiden i naar 8 bytes is echter een extra instructie op x64 vereist. Met IV-widening wordt de 64-bits JIT-compiler nu breder i tot 8 bytes gedurende de lus, waarbij de extensie nul wordt weggelaten. Het herhalen van matrices is zeer gebruikelijk en de voordelen van deze instructie verwijderen snel optellen.

Inlining verbeteringen voor systeemeigen AOT

Een van . De doelstellingen van NET voor de inliner van de 64-bits JIT-compiler zijn om zoveel mogelijk beperkingen te verwijderen waardoor een methode niet inline kan worden geplaatst. .NET 9 maakt het mogelijk om toegang te krijgen tot thread-lokale statics in Windows x64, Linux x64 en Linux Arm64.

Voor static klasseleden bestaat precies één exemplaar van het lid in alle exemplaren van de klasse, die het lid 'delen'. Als de waarde van een static lid uniek is voor elke thread, waardoor die waardethread-local de prestaties kan verbeteren, omdat er geen gelijktijdigheidsprimitief meer nodig is om veilig toegang te krijgen tot het lid vanuit de static bijbehorende thread.

Voorheen moest de 64-bits JIT-compiler toegang krijgen tot thread-lokale statics in systeemeigen AOT-gecompileerde programma's om een aanroep naar de runtime te verzenden om het basisadres van de thread-lokale opslag op te halen. Nu kan de compiler deze aanroepen inline plaatsen, wat resulteert in veel minder instructies voor toegang tot deze gegevens.

PGO-verbeteringen: Typecontroles en casts

.NET 8 ingeschakelde dynamische profielgestuurde optimalisatie (PGO) standaard. NET 9 breidt de 64-bits PGO-implementatie van de JIT-compiler uit om meer codepatronen te profilen. Wanneer gelaagde compilatie is ingeschakeld, voegt de 64-bits JIT-compiler al instrumentatie in uw programma in om het gedrag ervan te profilen. Wanneer deze opnieuw wordt gecompileerd met optimalisaties, maakt de compiler gebruik van het profiel dat tijdens runtime is gebouwd om beslissingen te nemen die specifiek zijn voor de huidige uitvoering van uw programma. In .NET 9 gebruikt de 64-bits JIT-compiler PGO-gegevens om de prestaties van typecontroles te verbeteren.

Het bepalen van het type van een object vereist een aanroep van de runtime, die wordt geleverd met een prestatiestraf. Wanneer het type van een object moet worden gecontroleerd, verzendt de 64-bits JIT-compiler deze aanroep omwille van juistheid (compilers kunnen meestal geen mogelijkheden uitsluiten, zelfs als ze onwaarschijnlijk lijken). Als PGO-gegevens echter suggereren dat een object waarschijnlijk een specifiek type is, verzendt de 64-bits JIT-compiler nu een snel pad dat goedkoop controleert op dat type en terugvalt op het langzame pad van het aanroepen naar de runtime, alleen indien nodig.

Arm64-vectorisatie in .NET-bibliotheken

Een nieuwe EncodeToUtf8 implementatie maakt gebruik van de 64-bits JIT-compiler om instructies voor het laden/opslaan van meerdere registraties op Arm64 te verzenden. Met dit gedrag kunnen programma's grotere stukken gegevens verwerken met minder instructies. .NET-apps in verschillende domeinen moeten doorvoerverbeteringen zien op Arm64-hardware die ondersteuning biedt voor deze functies. Sommige benchmarks hebben hun uitvoeringstijd met meer dan de helft verminderd.

.NET SDK

Het testen van modules

In deze sectie worden de updates voor eenheidstests in .NET 9 beschreven: parallelle tests uitvoeren en uitvoer van terminalloggertests.

Tests parallel uitvoeren

In .NET 9 is dotnet test het volledig geïntegreerd met MSBuild. Omdat MSBuild het bouwen parallel ondersteunt, kunt u tests uitvoeren voor hetzelfde project in verschillende doelframeworks parallel. Standaard beperkt MSBuild het aantal parallelle processen tot het aantal processors op de computer. U kunt ook uw eigen limiet instellen met behulp van de switch -maxcpucount . Als u zich wilt afmelden voor parallelle uitvoering, stelt u de TestTfmsInParallel eigenschap MSBuild in op false.

Testweergave terminallogger

Testresultatenrapportage voor dotnet test wordt nu rechtstreeks ondersteund in de MSBuild-terminallogger. U krijgt een volledigere testrapportage terwijl tests worden uitgevoerd (geeft de naam van de actieve test weer) en nadat de tests zijn voltooid (eventuele testfouten worden op een betere manier weergegeven).

Zie dotnet-buildopties voor meer informatie over de terminallogger.

Roll-forward van .NET-hulpprogramma

.NET-hulpprogramma's zijn frameworkafhankelijke apps die u globaal of lokaal kunt installeren en vervolgens kunt uitvoeren met behulp van de .NET SDK en geïnstalleerde .NET-runtimes. Deze hulpprogramma's, zoals alle .NET-apps, richten zich op een specifieke primaire versie van .NET. Apps worden standaard niet uitgevoerd op nieuwere versies van .NET. Auteurs van hulpprogramma's kunnen zich aanmelden om hun hulpprogramma's uit te voeren op nieuwere versies van de .NET-runtime door de RollForward eigenschap MSBuild in te stellen. Maar niet alle hulpprogramma's doen dit.

Met een nieuwe optie dotnet tool install kunnen gebruikers bepalen hoe .NET-hulpprogramma's moeten worden uitgevoerd. Wanneer u een hulpprogramma installeert via dotnet tool install, of wanneer u het hulpprogramma uitvoert via dotnet tool run <toolname>, kunt u een nieuwe vlag opgeven met de naam --allow-roll-forward. Met deze optie configureert u het hulpprogramma met de roll-forward-modus Major. Met deze modus kan het hulpprogramma worden uitgevoerd op een nieuwere primaire versie van .NET als de overeenkomende .NET-versie niet beschikbaar is. Met deze functie kunnen vroege gebruikers .NET-hulpprogramma's gebruiken zonder dat auteurs van hulpprogramma's code hoeven te wijzigen.

Zie ook