Delen via


Wat is er nieuw in de .NET 8-runtime

In dit artikel worden nieuwe functies in .NET Runtime voor .NET 8 beschreven.

Prestatieverbeteringen

.NET 8 bevat verbeteringen in het genereren van code en JIT-compilatie (Just-In-Time):

  • Prestatieverbeteringen van Arm64
  • SIMD-verbeteringen
  • Ondersteuning voor AVX-512 ISA-extensies (zie Vector512 en AVX-512)
  • Cloudeigen verbeteringen
  • JIT-doorvoerverbeteringen
  • Lus en algemene optimalisaties
  • Geoptimaliseerde toegang voor velden die zijn gemarkeerd met ThreadStaticAttribute
  • Opeenvolgende registratietoewijzing. Arm64 heeft twee instructies voor het opzoeken van tabelvectors. Hiervoor moeten alle entiteiten in hun tupleoperanden aanwezig zijn in opeenvolgende registers.
  • JIT/NativeAOT kan nu enkele geheugenbewerkingen opheffen en automatisch vectoriseren met SIMD, zoals vergelijking, kopiëren en nullen, als deze hun grootten tijdens het compileren kan bepalen.

Daarnaast is dynamische optimalisatie met profielgeleid (PGO) verbeterd en is nu standaard ingeschakeld. U hoeft geen runtimeconfiguratieoptie meer te gebruiken om deze in te schakelen. Dynamische PGO werkt hand-in-hand met gelaagde compilatie om code verder te optimaliseren op basis van extra instrumentatie die wordt uitgevoerd tijdens laag 0.

Gemiddeld verhoogt dynamische PGO de prestaties met ongeveer 15%. In een benchmarkpakket van ~4600 tests zag 23% prestatieverbeteringen van 20% of meer.

Codegen-structpromotie

.NET 8 bevat een nieuwe fysieke promotieoptimalisatiepas voor codegen waarmee de JIT-mogelijkheid wordt gegeneraliseerd om structvariabelen te promoten. Deze optimalisatie (ook wel scalaire vervanging van aggregaties genoemd) vervangt de velden van structvariabelen door primitieve variabelen die de JIT vervolgens kan berekent en nauwkeuriger kan optimaliseren.

De JIT biedt al ondersteuning voor deze optimalisatie, maar met verschillende grote beperkingen, waaronder:

  • Het werd alleen ondersteund voor structs met vier of minder velden.
  • Het werd alleen ondersteund als elk veld een primitief type was of een eenvoudige struct die een primitief type verpakt.

Met fysieke promotie worden deze beperkingen verwijderd, waardoor een aantal langdurige JIT-problemen wordt opgelost.

Garbagecollection

.NET 8 voegt een mogelijkheid toe om de geheugenlimiet op het moment aan te passen. Dit is handig in cloudservicescenario's, waar de vraag komt en gaat. Om rendabel te zijn, moeten services omhoog en omlaag schalen op het resourceverbruik naarmate de vraag fluctueert. Wanneer een service een afname van de vraag detecteert, kan het resourceverbruik omlaag worden geschaald door de geheugenlimiet te verminderen. Voorheen zou dit mislukken omdat de garbagecollector (GC) zich niet bewust was van de wijziging en mogelijk meer geheugen toewijst dan de nieuwe limiet. Met deze wijziging kunt u de RefreshMemoryLimit() API aanroepen om de GC bij te werken met de nieuwe geheugenlimiet.

Er zijn enkele beperkingen om rekening mee te houden:

  • Op 32-bits platforms (bijvoorbeeld Windows x86 en Linux ARM) kan .NET geen nieuwe heap harde limiet instellen als er nog geen limiet is.
  • De API retourneert mogelijk een niet-nulstatuscode die aangeeft dat het vernieuwen is mislukt. Dit kan gebeuren als de schaal te agressief is en geen ruimte laat voor de GC om te manoeuvreren. In dit geval kunt u aanroepen GC.Collect(2, GCCollectionMode.Aggressive) om het huidige geheugengebruik te verkleinen en het vervolgens opnieuw proberen.
  • Als u de geheugenlimiet omhoog schaalt buiten de grootte die de GC denkt dat het proces kan worden verwerkt tijdens het opstarten, slaagt de RefreshMemoryLimit aanroep, maar kan deze niet meer geheugen gebruiken dan wat het ziet als de limiet.

In het volgende codefragment ziet u hoe u de API aanroept.

GC.RefreshMemoryLimit();

U kunt ook enkele van de GC-configuratie-instellingen vernieuwen die betrekking hebben op de geheugenlimiet. Met het volgende codefragment wordt de harde heap-limiet ingesteld op 100 mebibytes (MiB):

AppContext.SetData("GCHeapHardLimit", (ulong)100 * 1_024 * 1_024);
GC.RefreshMemoryLimit();

De API kan een InvalidOperationException als de harde limiet ongeldig is, bijvoorbeeld in het geval van negatieve heap harde limietpercentages en als de harde limiet te laag is. Dit kan gebeuren als de heap-harde limiet die door de vernieuwing wordt ingesteld, ofwel vanwege nieuwe AppData-instellingen of impliciet door de wijzigingen in de geheugenlimiet van de container, lager is dan wat al is doorgevoerd.

Globalisatie voor mobiele apps

Mobiele apps op iOS, tvOS en MacCatalyst kunnen kiezen voor een nieuwe hybride globalisatiemodus die gebruikmaakt van een lichtere ICU-bundel. In de hybride modus worden globalisatiegegevens gedeeltelijk opgehaald uit de ICU-bundel en gedeeltelijk van aanroepen naar systeemeigen API's. De hybride modus dient voor alle landinstellingen die worden ondersteund door mobiele apparaten.

Hybride modus is het meest geschikt voor apps die niet kunnen werken in de invariante globaliseringsmodus en die gebruikmaken van culturen die zijn afgekapt van ICU-gegevens op mobiele apparaten. U kunt het ook gebruiken als u een kleiner ICU-gegevensbestand wilt laden. (Het icudt_hybrid.dat bestand is 34,5 % kleiner dan het standaard-ICU-gegevensbestand icudt.dat.)

Als u de hybride globalisatiemodus wilt gebruiken, stelt u de HybridGlobalization eigenschap MSBuild in op true:

<PropertyGroup>
  <HybridGlobalization>true</HybridGlobalization>
</PropertyGroup>

Er zijn enkele beperkingen om rekening mee te houden:

  • Vanwege beperkingen van systeemeigen API's worden niet alle globalisatie-API's ondersteund in de hybride modus.
  • Sommige ondersteunde API's hebben een ander gedrag.

Zie Gedragsverschillen om te controleren of uw toepassing wordt beïnvloed.

Door bron gegenereerde COM-interop

.NET 8 bevat een nieuwe brongenerator die ondersteuning biedt voor samenwerking met COM-interfaces. U kunt de GeneratedComInterfaceAttribute interface als com-interface markeren voor de brongenerator. De brongenerator genereert vervolgens code om aanroepen vanuit C#-code in te schakelen naar onbeheerde code. Er wordt ook code gegenereerd om aanroepen vanuit niet-beheerde code in C# mogelijk te maken. Deze brongenerator kan worden geïntegreerd met LibraryImportAttribute, en u kunt typen gebruiken met de GeneratedComInterfaceAttribute als parameters en retourtypen in LibraryImport-toegewezen methoden.

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
partial interface IComInterface
{
    void DoWork();
}

internal partial class MyNativeLib
{
    [LibraryImport(nameof(MyNativeLib))]
    public static partial void GetComInterface(out IComInterface comInterface);
}

De brongenerator ondersteunt ook het nieuwe GeneratedComClassAttribute kenmerk waarmee u typen kunt doorgeven die interfaces met het GeneratedComInterfaceAttribute kenmerk implementeren voor niet-beheerde code. De brongenerator genereert de code die nodig is om een COM-object beschikbaar te maken dat de interfaces implementeert en aanroepen doorstuurt naar de beheerde implementatie.

Methoden voor interfaces met het GeneratedComInterfaceAttribute kenmerk ondersteunen dezelfde typen als LibraryImportAttribute, en LibraryImportAttribute ondersteunen GeneratedComInterfacenu -toegewezen typen en GeneratedComClasstoegewezen typen.

Als uw C#-code alleen een GeneratedComInterfacedoor u toegewezen interface gebruikt om een COM-object te verpakken van niet-beheerde code of een beheerd object van C# verpakken om deze beschikbaar te maken voor onbeheerde code, kunt u de opties in de Options eigenschap gebruiken om aan te passen welke code wordt gegenereerd. Deze opties betekenen dat u geen marshallers hoeft te schrijven voor scenario's die u weet dat ze niet worden gebruikt.

De brongenerator gebruikt het nieuwe StrategyBasedComWrappers type om de COM-objectwikkelaars en de beheerde objectwikkelaars te maken en te beheren. Dit nieuwe type verwerkt de verwachte .NET-gebruikerservaring voor COM-interoperabiliteit en biedt aanpassingspunten voor geavanceerde gebruikers. Als uw toepassing een eigen mechanisme heeft voor het definiëren van typen van COM of als u scenario's wilt ondersteunen die momenteel door de bron gegenereerde COM niet ondersteunen, kunt u overwegen het nieuwe StrategyBasedComWrappers type te gebruiken om de ontbrekende functies voor uw scenario toe te voegen en dezelfde .NET-gebruikerservaring voor uw COM-typen te krijgen.

Als u Visual Studio gebruikt, kunt u met nieuwe analyses en codecorrecties uw bestaande COM-interop-code eenvoudig converteren om brongegenereerde interop te gebruiken. Naast elke interface met de ComImportAttribute, biedt een gloeilamp een optie om te converteren naar bron gegenereerde interop. De oplossing wijzigt de interface om het GeneratedComInterfaceAttribute kenmerk te gebruiken. Naast elke klasse waarmee GeneratedComInterfaceAttributeeen interface wordt geïmplementeerd, biedt een gloeilamp een optie om het GeneratedComClassAttribute kenmerk toe te voegen aan het type. Zodra uw typen zijn geconverteerd, kunt u uw DllImport methoden verplaatsen om te gebruiken LibraryImportAttribute.

Beperkingen

De COM-brongenerator biedt geen ondersteuning voor affiniteit met appartementen, waarbij het new trefwoord wordt gebruikt om een COM CoClass te activeren en de volgende API's:

  • IDispatch-gebaseerde interfaces.
  • IInspectable-gebaseerde interfaces.
  • COM-eigenschappen en -gebeurtenissen.

Configuratiebindingsbrongenerator

.NET 8 introduceert een brongenerator om AOT en trimvriendelijke configuratie te bieden in ASP.NET Core. De generator is een alternatief voor de reeds bestaande op reflectie gebaseerde implementatie.

De brongenerator test op Configure(TOptions), Binden Get aanroepen om typegegevens op te halen. Wanneer de generator is ingeschakeld in een project, kiest de compiler impliciet gegenereerde methoden voor de bestaande op reflectie gebaseerde framework-implementaties.

Er zijn geen broncodewijzigingen nodig om de generator te kunnen gebruiken. Deze functie is standaard ingeschakeld in AOT'd-web-apps. Voor andere projecttypen is de brongenerator standaard uitgeschakeld, maar u kunt ervoor kiezen om de EnableConfigurationBindingGenerator eigenschap in uw projectbestand in te true stellen:

<PropertyGroup>
    <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

De volgende code toont een voorbeeld van het aanroepen van de binder.

public class ConfigBindingSG
{
    static void RunIt(params string[] args)
    {
        WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
        IConfigurationSection section = builder.Configuration.GetSection("MyOptions");

        // !! Configure call - to be replaced with source-gen'd implementation
        builder.Services.Configure<MyOptions>(section);

        // !! Get call - to be replaced with source-gen'd implementation
        MyOptions? options0 = section.Get<MyOptions>();

        // !! Bind call - to be replaced with source-gen'd implementation
        MyOptions options1 = new();
        section.Bind(options1);

        WebApplication app = builder.Build();
        app.MapGet("/", () => "Hello World!");
        app.Run();
    }

    public class MyOptions
    {
        public int A { get; set; }
        public string S { get; set; }
        public byte[] Data { get; set; }
        public Dictionary<string, string> Values { get; set; }
        public List<MyClass> Values2 { get; set; }
    }

    public class MyClass
    {
        public int SomethingElse { get; set; }
    }
}

Core .NET-bibliotheken

Deze sectie bevat de volgende subonderwerpen:

Reflectie

Functiepointers zijn geïntroduceerd in .NET 5, maar de bijbehorende ondersteuning voor weerspiegeling is op dat moment niet toegevoegd. Wanneer u typeof bijvoorbeeld een functieaanwijzer typeof(delegate*<void>()) gebruikt of weerspiegelt, is FieldInfo.FieldType er een IntPtr geretourneerd. Vanaf .NET 8 wordt in plaats daarvan een System.Type object geretourneerd. Dit type biedt toegang tot metagegevens van functie-aanwijzers, waaronder de aanroepende conventies, het retourtype en de parameters.

Notitie

Een functieaanwijzerexemplaren, een fysiek adres voor een functie, worden nog steeds weergegeven als een IntPtr. Alleen het weerspiegelingstype is gewijzigd.

De nieuwe functionaliteit wordt momenteel alleen geïmplementeerd in de CoreCLR-runtime en MetadataLoadContext.

Er zijn nieuwe API's toegevoegd aan System.Type, zoals IsFunctionPointer, en aan System.Reflection.PropertyInfo, System.Reflection.FieldInfoen System.Reflection.ParameterInfo. De volgende code laat zien hoe u enkele van de nieuwe API's gebruikt voor reflectie.

using System;
using System.Reflection;

// Sample class that contains a function pointer field.
public unsafe class UClass
{
    public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void> _fp;
}

internal class FunctionPointerReflection
{
    public static void RunIt()
    {
        FieldInfo? fieldInfo = typeof(UClass).GetField(nameof(UClass._fp));

        // Obtain the function pointer type from a field.
        Type? fpType = fieldInfo?.FieldType;

        // New methods to determine if a type is a function pointer.
        Console.WriteLine(
        $"IsFunctionPointer: {fpType?.IsFunctionPointer}");
        Console.WriteLine(
            $"IsUnmanagedFunctionPointer: {fpType?.IsUnmanagedFunctionPointer}");

        // New methods to obtain the return and parameter types.
        Console.WriteLine($"Return type: {fpType?.GetFunctionPointerReturnType()}");

        if (fpType is not null)
        {
            foreach (Type parameterType in fpType.GetFunctionPointerParameterTypes())
            {
                Console.WriteLine($"Parameter type: {parameterType}");
            }
        }

        // Access to custom modifiers and calling conventions requires a "modified type".
        Type? modifiedType = fieldInfo?.GetModifiedFieldType();

        // A modified type forwards most members to its underlying type.
        Type? normalType = modifiedType?.UnderlyingSystemType;

        if (modifiedType is not null)
        {
            // New method to obtain the calling conventions.
            foreach (Type callConv in modifiedType.GetFunctionPointerCallingConventions())
            {
                Console.WriteLine($"Calling convention: {callConv}");
            }
        }

        // New method to obtain the custom modifiers.
        Type[]? modifiers =
            modifiedType?.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers();

        if (modifiers is not null)
        {
            foreach (Type modreq in modifiers)
            {
                Console.WriteLine($"Required modifier for first parameter: {modreq}");
            }
        }
    }
}

In het vorige voorbeeld wordt de volgende uitvoer gegenereerd:

IsFunctionPointer: True
IsUnmanagedFunctionPointer: True
Return type: System.Void
Parameter type: System.Int32&
Calling convention: System.Runtime.CompilerServices.CallConvSuppressGCTransition
Calling convention: System.Runtime.CompilerServices.CallConvCdecl
Required modifier for first parameter: System.Runtime.InteropServices.InAttribute

Serialization

Er zijn veel verbeteringen aangebracht in System.Text.Json de serialisatie- en deserialisatiefunctionaliteit in .NET 8. U kunt bijvoorbeeld de verwerking aanpassen van leden die zich niet in de JSON-nettolading hebben.

In de volgende secties worden andere serialisatieverbeteringen beschreven:

Zie JSON-serialisatie en deserialisatie in .NET voor meer informatie over JSON-serialisatie in het algemeen.

Ingebouwde ondersteuning voor extra typen

De serializer biedt ingebouwde ondersteuning voor de volgende extra typen.

  • Half, Int128en UInt128 numerieke typen.

    Console.WriteLine(JsonSerializer.Serialize(
        [ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ]
    ));
    // [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]
    
  • Memory<T> en ReadOnlyMemory<T> waarden. byte waarden worden geserialiseerd naar Base64-tekenreeksen en andere typen voor JSON-matrices.

    JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 }); // "AQID"
    JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); // [1,2,3]
    

Brongenerator

.NET 8 bevat verbeteringen van de System.Text.Json-brongenerator die zijn gericht op het maken van de Systeemeigen AOT-ervaring op basis van de serializer op basis van reflectie. Voorbeeld:

  • De brongenerator ondersteunt nu serialisatietypen met required en init eigenschappen. Deze werden beide al ondersteund in serialisatie op basis van reflectie.

  • Verbeterde opmaak van door de broncode gegenereerde code.

  • JsonSourceGenerationOptionsAttribute functiepariteit met JsonSerializerOptions. Zie Opties opgeven (brongeneratie) voor meer informatie.

  • Aanvullende diagnostische gegevens (zoals SYSLIB1034 en SYSLIB1039).

  • Neem geen typen genegeerde of niet-toegankelijke eigenschappen op.

  • Ondersteuning voor geneste JsonSerializerContext declaraties binnen willekeurige typen.

  • Ondersteuning voor door compiler gegenereerde of onpeakbare typen in zwak getypeerde brongeneratiescenario's. Omdat door compiler gegenereerde typen niet expliciet kunnen worden opgegeven door de brongenerator, System.Text.Json wordt nu de dichtstbijzijnde bovenliggende resolutie uitgevoerd tijdens runtime. Deze resolutie bepaalt het meest geschikte supertype waarmee de waarde moet worden geserialiseerd.

  • Nieuw type JsonStringEnumConverter<TEnum>conversieprogramma. De bestaande JsonStringEnumConverter klasse wordt niet ondersteund in systeemeigen AOT. U kunt als volgt aantekeningen toevoegen aan uw opsommingstypen:

    [JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
    public enum MyEnum { Value1, Value2, Value3 }
    
    [JsonSerializable(typeof(MyEnum))]
    public partial class MyContext : JsonSerializerContext { }
    

    Zie Opsommingsvelden serialiseren als tekenreeksen voor meer informatie.

  • Met de nieuwe JsonConverter.Type eigenschap kunt u het type van een niet-algemeen JsonConverter exemplaar opzoeken:

    Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters)
        => converters.Where(converter => converter.Type != null)
                     .ToDictionary(converter => converter.Type!);
    

    De eigenschap kan null worden omdat deze wordt geretourneerd null voor JsonConverterFactory exemplaren en typeof(T) voor JsonConverter<T> exemplaren.

Ketenbrongeneratoren

De JsonSerializerOptions klasse bevat een nieuwe TypeInfoResolverChain eigenschap die een aanvulling vormt op de bestaande TypeInfoResolver eigenschap. Deze eigenschappen worden gebruikt in contractaanpassing voor het koppelen van brongeneratoren. De toevoeging van de nieuwe eigenschap betekent dat u niet alle gekoppelde onderdelen op één oproepsite hoeft op te geven. Ze kunnen na het feit worden toegevoegd. TypeInfoResolverChain U kunt ook de keten introspect of onderdelen ervan verwijderen. Zie Brongeneratoren combineren voor meer informatie.

Bovendien JsonSerializerOptions.AddContext<TContext>() is het nu verouderd. Het is vervangen door de TypeInfoResolver en TypeInfoResolverChain eigenschappen. Zie SYSLIB0049 voor meer informatie.

Interfacehiërarchieën

.NET 8 voegt ondersteuning toe voor het serialiseren van eigenschappen uit interfacehiërarchieën.

De volgende code toont een voorbeeld waarin de eigenschappen van zowel de direct geïmplementeerde interface als de basisinterface worden geserialiseerd.

public static void InterfaceHierarchies()
{
    IDerived value = new DerivedImplement { Base = 0, Derived = 1 };
    string json = JsonSerializer.Serialize(value);
    Console.WriteLine(json); // {"Derived":1,"Base":0}
}

public interface IBase
{
    public int Base { get; set; }
}

public interface IDerived : IBase
{
    public int Derived { get; set; }
}

public class DerivedImplement : IDerived
{
    public int Base { get; set; }
    public int Derived { get; set; }
}

Naamgevingsbeleid

JsonNamingPolicy bevat nieuwe naamgevingsbeleidsregels voor snake_case (met een onderstrepingsteken) en kebab-case (met een afbreekstreepje) eigenschappennaamconversies. Gebruik deze beleidsregels op dezelfde manier als het bestaande JsonNamingPolicy.CamelCase beleid:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
JsonSerializer.Serialize(new { PropertyName = "value" }, options);
// { "property_name" : "value" }

Zie Een ingebouwd naamgevingsbeleid gebruiken voor meer informatie.

Alleen-lezen eigenschappen

U kunt nu deserialiseren naar alleen-lezen velden of eigenschappen (dat wil gezegd, velden die geen toegangsfunctie hebben set ).

Als u zich wereldwijd wilt aanmelden voor deze ondersteuning, stelt u een nieuwe optie in PreferredObjectCreationHandlingJsonObjectCreationHandling.Populateop . Als compatibiliteit een probleem is, kunt u de functionaliteit ook gedetailleerder inschakelen door het [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] kenmerk te plaatsen op specifieke typen waarvan de eigenschappen moeten worden ingevuld of op afzonderlijke eigenschappen.

Denk bijvoorbeeld aan de volgende code die wordt gedeserialiseerd in een CustomerInfo type met twee alleen-lezen eigenschappen.

public static void ReadOnlyProperties()
{
    CustomerInfo customer = JsonSerializer.Deserialize<CustomerInfo>("""
        { "Names":["John Doe"], "Company":{"Name":"Contoso"} }
        """)!;

    Console.WriteLine(JsonSerializer.Serialize(customer));
}

class CompanyInfo
{
    public required string Name { get; set; }
    public string? PhoneNumber { get; set; }
}

[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class CustomerInfo
{
    // Both of these properties are read-only.
    public List<string> Names { get; } = new();
    public CompanyInfo Company { get; } = new()
    {
        Name = "N/A",
        PhoneNumber = "N/A"
    };
}

Vóór .NET 8 werden de invoerwaarden genegeerd en behouden de NamesCompany eigenschappen hun standaardwaarden.

{"Names":[],"Company":{"Name":"N/A","PhoneNumber":"N/A"}}

De invoerwaarden worden nu gebruikt om de alleen-lezeneigenschappen te vullen tijdens de deserialisatie.

{"Names":["John Doe"],"Company":{"Name":"Contoso","PhoneNumber":"N/A"}}

Zie Geïnitialiseerde eigenschappen vullen voor meer informatie over het gedrag voor het vullen van deserialisatie.

Standaardinstelling voor weerspiegeling uitschakelen

U kunt nu standaard uitschakelen met behulp van de serializer op basis van weerspiegeling. Deze uitschakeling is handig om onbedoelde rooting van weerspiegelingsonderdelen te voorkomen die niet eens in gebruik zijn, met name in ingekorte en systeemeigen AOT-apps. Als u standaard serialisatie op basis van weerspiegeling wilt uitschakelen door te vereisen dat een JsonSerializerOptions argument wordt doorgegeven aan de JsonSerializer serialisatie- en deserialisatiemethoden, stelt u de JsonSerializerIsReflectionEnabledByDefault eigenschap false MSBuild in op in uw projectbestand.

Gebruik de nieuwe IsReflectionEnabledByDefault API om de waarde van de functieswitch te controleren. Als u een auteur van een bibliotheek System.Text.Jsonbent, kunt u vertrouwen op de eigenschap om uw standaardinstellingen te configureren zonder dat u per ongeluk weerspiegelingsonderdelen hoeft te rooten.

Zie Standaardinstellingen voor weerspiegeling uitschakelen voor meer informatie.

Nieuwe JsonNode-API-methoden

De JsonNode en System.Text.Json.Nodes.JsonArray typen bevatten de volgende nieuwe methoden.

public partial class JsonNode
{
    // Creates a deep clone of the current node and all its descendants.
    public JsonNode DeepClone();

    // Returns true if the two nodes are equivalent JSON representations.
    public static bool DeepEquals(JsonNode? node1, JsonNode? node2);

    // Determines the JsonValueKind of the current node.
    public JsonValueKind GetValueKind(JsonSerializerOptions options = null);

    // If node is the value of a property in the parent
    // object, returns its name.
    // Throws InvalidOperationException otherwise.
    public string GetPropertyName();

    // If node is the element of a parent JsonArray,
    // returns its index.
    // Throws InvalidOperationException otherwise.
    public int GetElementIndex();

    // Replaces this instance with a new value,
    // updating the parent object/array accordingly.
    public void ReplaceWith<T>(T value);

    // Asynchronously parses a stream as UTF-8 encoded data
    // representing a single JSON value into a JsonNode.
    public static Task<JsonNode?> ParseAsync(
        Stream utf8Json,
        JsonNodeOptions? nodeOptions = null,
        JsonDocumentOptions documentOptions = default,
        CancellationToken cancellationToken = default);
}

public partial class JsonArray
{
    // Returns an IEnumerable<T> view of the current array.
    public IEnumerable<T> GetValues<T>();
}

Niet-openbare leden

U kunt niet-openbare leden kiezen voor het serialisatiecontract voor een bepaald type met behulp van JsonIncludeAttribute en JsonConstructorAttribute kenmerkaantekeningen.

public static void NonPublicMembers()
{
    string json = JsonSerializer.Serialize(new MyPoco(42));
    Console.WriteLine(json);
    // {"X":42}

    JsonSerializer.Deserialize<MyPoco>(json);
}

public class MyPoco
{
    [JsonConstructor]
    internal MyPoco(int x) => X = x;

    [JsonInclude]
    internal int X { get; }
}

Zie Onveranderbare typen en niet-openbare leden en accessors gebruiken voor meer informatie.

Streamingdeserialisatie-API's

.NET 8 bevat bijvoorbeeld GetFromJsonAsAsyncEnumerablenieuwe IAsyncEnumerable<T> uitbreidingsmethoden voor streamingdeserialisatie. Vergelijkbare methoden bestaan die bijvoorbeeld HttpClientJsonExtensions.GetFromJsonAsyncretournerenTask<TResult>. De nieuwe extensiemethoden roepen streaming-API's aan en retourneren IAsyncEnumerable<T>.

De volgende code laat zien hoe u de nieuwe extensiemethoden kunt gebruiken.

public async static void StreamingDeserialization()
{
    const string RequestUri = "https://api.contoso.com/books";
    using var client = new HttpClient();
    IAsyncEnumerable<Book?> books = client.GetFromJsonAsAsyncEnumerable<Book>(RequestUri);

    await foreach (Book? book in books)
    {
        Console.WriteLine($"Read book '{book?.title}'");
    }
}

public record Book(int id, string title, string author, int publishedYear);

Extensiemethode WithAddedModifier

Met de nieuwe WithAddedModifier(IJsonTypeInfoResolver, Action<JsonTypeInfo>) extensiemethode kunt u eenvoudig wijzigingen aanbrengen in de serialisatiecontracten van willekeurige IJsonTypeInfoResolver exemplaren.

var options = new JsonSerializerOptions
{
    TypeInfoResolver = MyContext.Default
        .WithAddedModifier(static typeInfo =>
        {
            foreach (JsonPropertyInfo prop in typeInfo.Properties)
            {
                prop.Name = prop.Name.ToUpperInvariant();
            }
        })
};

Nieuwe JsonContent.Create overloads

U kunt nu exemplaren maken JsonContent met trim-safe of door bron gegenereerde contracten. De nieuwe methoden zijn:

var book = new Book(id: 42, "Title", "Author", publishedYear: 2023);
HttpContent content = JsonContent.Create(book, MyContext.Default.Book);

public record Book(int id, string title, string author, int publishedYear);

[JsonSerializable(typeof(Book))]
public partial class MyContext : JsonSerializerContext
{
}

Een JsonSerializerOptions-exemplaar blokkeren

Met de volgende nieuwe methoden kunt u bepalen wanneer een JsonSerializerOptions exemplaar is geblokkeerd:

  • JsonSerializerOptions.MakeReadOnly()

    Deze overbelasting is ontworpen om veilig te zijn en genereert daarom een uitzondering in gevallen waarin het exemplaar van de opties niet is geconfigureerd met een resolver.

  • JsonSerializerOptions.MakeReadOnly(Boolean)

    Als u deze overbelasting doorgeeft true , wordt het exemplaar van de opties gevuld met de standaardoverspiegelingsopzetting als er een ontbreekt. Deze methode is gemarkeerd RequiresUnreferenceCode/RequiresDynamicCode en is daarom niet geschikt voor systeemeigen AOT-toepassingen.

Met de nieuwe IsReadOnly eigenschap kunt u controleren of het exemplaar van de opties is geblokkeerd.

Tijdabstractie

Met de nieuwe TimeProvider klasse en ITimer interface voegt u de functionaliteit voor tijdabstractie toe, waarmee u tijd kunt mocken in testscenario's. Daarnaast kunt u de tijdabstractie gebruiken om bewerkingen te mocken Task die afhankelijk zijn van de voortgang van de tijd en Task.DelayTask.WaitAsync. De tijdabstractie ondersteunt de volgende essentiële tijdbewerkingen:

  • Lokale en UTC-tijd ophalen
  • Een tijdstempel verkrijgen voor het meten van prestaties
  • Een timer maken

In het volgende codefragment ziet u enkele gebruiksvoorbeelden.

// Get system time.
DateTimeOffset utcNow = TimeProvider.System.GetUtcNow();
DateTimeOffset localNow = TimeProvider.System.GetLocalNow();

TimerCallback callback = s => ((State)s!).Signal();

// Create a timer using the time provider.
ITimer timer = _timeProvider.CreateTimer(
    callback, null, TimeSpan.Zero, Timeout.InfiniteTimeSpan);

// Measure a period using the system time provider.
long providerTimestamp1 = TimeProvider.System.GetTimestamp();
long providerTimestamp2 = TimeProvider.System.GetTimestamp();

TimeSpan period = _timeProvider.GetElapsedTime(providerTimestamp1, providerTimestamp2);
// Create a time provider that works with a
// time zone that's different than the local time zone.
private class ZonedTimeProvider(TimeZoneInfo zoneInfo) : TimeProvider()
{
    private readonly TimeZoneInfo _zoneInfo = zoneInfo ?? TimeZoneInfo.Local;

    public override TimeZoneInfo LocalTimeZone => _zoneInfo;

    public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) =>
        new ZonedTimeProvider(zoneInfo);
}

UTF8-verbeteringen

Als u het schrijven van een tekenreeksachtige weergave van uw type wilt inschakelen naar een doelbereik, implementeert u de nieuwe IUtf8SpanFormattable interface op uw type. Deze nieuwe interface is nauw verwant aan ISpanFormattable, maar is gericht op UTF8 en Span<byte> in plaats van UTF16 en Span<char>.

IUtf8SpanFormattable is geïmplementeerd op alle primitieve typen (plus andere), met exact dezelfde gedeelde logica, ongeacht of het gaat stringom , Span<char>of Span<byte>. Het heeft volledige ondersteuning voor alle indelingen (inclusief de nieuwe binaire aanduiding B) en alle culturen. Dit betekent dat u nu rechtstreeks naar UTF8 kunt opmaken vanaf Byte, , , Char, DecimalTimeOnlyInt64UInt32Int32TimeSpanInt16UInt16SingleIntPtrIPAddressDateOnlyDoubleHalfDateTimeGuidInt128SByteIPNetworkRuneNFloatDateTimeOffset, , , UInt64, en . UInt128UIntPtrVersionComplex

Nieuwe Utf8.TryWrite methoden bieden een op UTF8 gebaseerde tegenhanger voor de bestaande MemoryExtensions.TryWrite methoden, die op UTF16 zijn gebaseerd. U kunt geïnterpoleerde tekenreekssyntaxis gebruiken om een complexe expressie rechtstreeks in een bereik van UTF8 bytes op te maken, bijvoorbeeld:

static bool FormatHexVersion(
    short major,
    short minor,
    short build,
    short revision,
    Span<byte> utf8Bytes,
    out int bytesWritten) =>
    Utf8.TryWrite(
        utf8Bytes,
        CultureInfo.InvariantCulture,
        $"{major:X4}.{minor:X4}.{build:X4}.{revision:X4}",
        out bytesWritten);

De implementatie herkent IUtf8SpanFormattable de notatiewaarden en gebruikt hun implementaties om hun UTF8-representaties rechtstreeks naar het doelbereik te schrijven.

De implementatie maakt ook gebruik van de nieuwe Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32) methode, die samen met zijn Encoding.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32) tegenhanger ondersteuning biedt voor codering en decodering in een doelbereik. Als de periode niet lang genoeg is om de resulterende status te behouden, retourneren false de methoden in plaats van een uitzondering te genereren.

Methoden voor het werken met willekeurigheid

De System.Random en System.Security.Cryptography.RandomNumberGenerator typen introduceren twee nieuwe methoden voor het werken met willekeurigheid.

GetItems<T>()

Met de nieuwe System.Random.GetItems methoden System.Security.Cryptography.RandomNumberGenerator.GetItems kunt u willekeurig een opgegeven aantal items uit een invoerset kiezen. In het volgende voorbeeld ziet u hoe System.Random.GetItems<T>() u (op het exemplaar van de Random.Shared eigenschap) willekeurig 31 items in een matrix kunt invoegen. Dit voorbeeld kan worden gebruikt in een spel van 'Simon' waar spelers een reeks gekleurde knoppen moeten onthouden.

private static ReadOnlySpan<Button> s_allButtons = new[]
{
    Button.Red,
    Button.Green,
    Button.Blue,
    Button.Yellow,
};

// ...

Button[] thisRound = Random.Shared.GetItems(s_allButtons, 31);
// Rest of game goes here ...

Shuffle<T>()

Met de nieuwe Random.Shuffle methoden RandomNumberGenerator.Shuffle<T>(Span<T>) kunt u de volgorde van een bereik willekeurig maken. Deze methoden zijn handig voor het verminderen van trainingsvooroordelen in machine learning (dus het eerste is niet altijd trainen en het laatste wat altijd wordt getest).

YourType[] trainingData = LoadTrainingData();
Random.Shared.Shuffle(trainingData);

IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);

DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData);
model = chain.Fit(split.TrainSet);

IDataView predictions = model.Transform(split.TestSet);
// ...

Prestatiegerichte typen

.NET 8 introduceert verschillende nieuwe typen die zijn gericht op het verbeteren van de app-prestaties.

  • De nieuwe System.Collections.Frozen naamruimte bevat de verzamelingstypen FrozenDictionary<TKey,TValue> en FrozenSet<T>. Deze typen staan geen wijzigingen in sleutels en waarden toe zodra een verzameling is gemaakt. Deze vereiste maakt snellere leesbewerkingen mogelijk (bijvoorbeeld TryGetValue()). Deze typen zijn met name handig voor verzamelingen die worden ingevuld bij het eerste gebruik en vervolgens worden bewaard voor de duur van een service met een lange levensduur, bijvoorbeeld:

    private static readonly FrozenDictionary<string, bool> s_configurationData =
        LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
    
    // ...
    if (s_configurationData.TryGetValue(key, out bool setting) && setting)
    {
        Process();
    }
    
  • Methoden zoals MemoryExtensions.IndexOfAny zoeken naar het eerste exemplaar van een waarde in de doorgegeven verzameling. Het nieuwe System.Buffers.SearchValues<T> type is ontworpen om aan dergelijke methoden te worden doorgegeven. In .NET 8 worden nieuwe overbelastingen van methoden toegevoegd, zoals MemoryExtensions.IndexOfAny die een exemplaar van het nieuwe type accepteren. Wanneer u een exemplaar maakt vanSearchValues<T>, worden alle gegevens die nodig zijn om de volgende zoekopdrachten te optimaliseren op dat moment afgeleid, wat betekent dat het werk vooraf wordt uitgevoerd.

  • Het nieuwe System.Text.CompositeFormat type is handig voor het optimaliseren van notatietekenreeksen die niet bekend zijn tijdens het compileren (bijvoorbeeld als de notatietekenreeks uit een resourcebestand wordt geladen). Een beetje extra tijd wordt vooraf besteed aan het uitvoeren van werk, zoals het parseren van de tekenreeks, maar het bespaart het werk van elke toepassing.

    private static readonly CompositeFormat s_rangeMessage =
        CompositeFormat.Parse(LoadRangeMessageResource());
    
    // ...
    static string GetMessage(int min, int max) =>
        string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
    
  • Nieuwe System.IO.Hashing.XxHash3 typen System.IO.Hashing.XxHash128 bieden implementaties van de snelle XXH3- en XXH128-hashalgoritmen.

System.Numerics en System.Runtime.Intrinsics

In deze sectie worden verbeteringen in de System.Numerics en System.Runtime.Intrinsics naamruimten behandeld.

  • Vector256<T>, Matrix3x2en Matrix4x4 hebben verbeterde hardwareversnelling op .NET 8. Er is bijvoorbeeld Vector256<T> voor interne bewerkingen 2x Vector128<T> een nieuwe bewerking uitgevoerd, indien mogelijk. Dit maakt gedeeltelijke versnelling van sommige functies mogelijk wanneer Vector128.IsHardwareAccelerated == true maar Vector256.IsHardwareAccelerated == false, zoals op Arm64.
  • Hardware-intrinsieke kenmerken worden nu geannoteerd met het ConstExpected kenmerk. Dit zorgt ervoor dat gebruikers zich bewust zijn wanneer de onderliggende hardware een constante verwacht en daarom wanneer een niet-constante waarde de prestaties onverwacht kan schaden.
  • De Lerp(TSelf, TSelf, TSelf)Lerp API is toegevoegd aan IFloatingPointIeee754<TSelf> en daarom aan float (Single), double (Double) en Half. Met deze API kan een lineaire interpolatie tussen twee waarden efficiënt en correct worden uitgevoerd.

Vector512 en AVX-512

Uitgebreide SIMD-ondersteuning voor .NET Core 3.0 om de platformspecifieke hardware-API's voor x86/x64 op te nemen. .NET 5 toegevoegde ondersteuning voor Arm64 en .NET 7 heeft de intrinsiek van platformoverschrijdende hardware toegevoegd. .NET 8 zorgt voor SIMD-ondersteuning door instructies voor Intel Advanced Vector Extensions 512 (AVX-512) in te voeren Vector512<T> en te ondersteunen.

In het bijzonder bevat .NET 8 ondersteuning voor de volgende belangrijke functies van AVX-512:

  • 512-bits vectorbewerkingen
  • Extra 16 SIMD-registers
  • Aanvullende instructies beschikbaar voor 128-bits, 256-bits en 512-bits vectoren

Als u hardware hebt die ondersteuning biedt voor de functionaliteit, rapporteert trueu Vector512.IsHardwareAccelerated nu .

.NET 8 voegt ook verschillende platformspecifieke klassen toe onder de System.Runtime.Intrinsics.X86 naamruimte:

Deze klassen volgen dezelfde algemene vorm als andere isa's (instructiesetarchitecturen) omdat ze een IsSupported eigenschap en een geneste Avx512F.X64 klasse beschikbaar maken voor instructies die alleen beschikbaar zijn voor 64-bits processen. Daarnaast heeft elke klasse een geneste Avx512F.VL klasse waarmee de Avx512VL extensies (vectorlengte) voor de bijbehorende instructieset worden weergegeven.

Zelfs als u niet expliciet -specifieke of Avx512F-specifieke instructies in uw code gebruiktVector512, profiteert u waarschijnlijk nog steeds van de nieuwe AVX-512-ondersteuning. De JIT kan impliciet profiteren van de aanvullende registers en instructies bij gebruik Vector128<T> of Vector256<T>. De basisklassebibliotheek maakt gebruik van deze hardwareintrinsieken intern in de meeste bewerkingen die worden weergegeven door Span<T> en ReadOnlySpan<T> in veel van de wiskundige API's die beschikbaar zijn voor de primitieve typen.

Gegevensvalidatie

De System.ComponentModel.DataAnnotations naamruimte bevat nieuwe kenmerken voor gegevensvalidatie die zijn bedoeld voor validatiescenario's in cloudeigen services. Hoewel de bestaande DataAnnotations validators zijn afgestemd op typische validatie van gebruikersinterfacegegevensinvoer, zoals velden in een formulier, zijn de nieuwe kenmerken ontworpen om niet-gebruikersinvoergegevens te valideren, zoals configuratieopties. Naast de nieuwe kenmerken zijn er nieuwe eigenschappen toegevoegd aan de RangeAttribute en RequiredAttribute typen.

Nieuwe API Beschrijving
RangeAttribute.MinimumIsExclusive
RangeAttribute.MaximumIsExclusive
Hiermee geeft u op of de grenzen zijn opgenomen in het toegestane bereik.
System.ComponentModel.DataAnnotations.LengthAttribute Hiermee geeft u zowel onder- als bovengrenzen voor tekenreeksen of verzamelingen op. Vereist bijvoorbeeld [Length(10, 20)] ten minste 10 elementen en maximaal 20 elementen in een verzameling.
System.ComponentModel.DataAnnotations.Base64StringAttribute Valideert dat een tekenreeks een geldige Base64-weergave is.
System.ComponentModel.DataAnnotations.AllowedValuesAttribute
System.ComponentModel.DataAnnotations.DeniedValuesAttribute
Geef respectievelijk acceptatielijsten en lijsten voor weigeren op. Bijvoorbeeld [AllowedValues("apple", "banana", "mango")].

Metrische gegevens voor

Met nieuwe API's kunt u tags koppelen aan Meter sleutel-waardepaar en Instrument objecten wanneer u ze maakt. Aggregators van gepubliceerde metrische metingen kunnen de tags gebruiken om de geaggregeerde waarden te onderscheiden.

var options = new MeterOptions("name")
{
    Version = "version",
    // Attach these tags to the created meter.
    Tags = new TagList()
    {
        { "MeterKey1", "MeterValue1" },
        { "MeterKey2", "MeterValue2" }
    }
};

Meter meter = meterFactory!.Create(options);

Counter<int> counterInstrument = meter.CreateCounter<int>(
    "counter", null, null, new TagList() { { "counterKey1", "counterValue1" } }
);
counterInstrument.Add(1);

De nieuwe API's zijn onder andere:

Cryptografie

.NET 8 voegt ondersteuning toe voor de SHA-3 hashing primitieven. (SHA-3 wordt momenteel ondersteund door Linux met OpenSSL 1.1.1 of hoger en Windows 11 Build 25324 of hoger.) API's waar SHA-2 beschikbaar is, bieden nu een SHA-3-compliment. Dit omvatSHA3_256, SHA3_384en SHA3_512 voor hashing; HMACSHA3_256, HMACSHA3_384en HMACSHA3_512 voor HMAC; HashAlgorithmName.SHA3_256, HashAlgorithmName.SHA3_384en HashAlgorithmName.SHA3_512 voor hashing waar het algoritme kan worden geconfigureerd; en , RSAEncryptionPadding.OaepSHA3_384en RSAEncryptionPadding.OaepSHA3_256RSAEncryptionPadding.OaepSHA3_512 voor RSA OAEP-versleuteling.

In het volgende voorbeeld ziet u hoe u de API's gebruikt, inclusief de SHA3_256.IsSupported eigenschap om te bepalen of het platform SHA-3 ondersteunt.

// Hashing example
if (SHA3_256.IsSupported)
{
    byte[] hash = SHA3_256.HashData(dataToHash);
}
else
{
    // ...
}

// Signing example
if (SHA3_256.IsSupported)
{
     using ECDsa ec = ECDsa.Create(ECCurve.NamedCurves.nistP256);
     byte[] signature = ec.SignData(dataToBeSigned, HashAlgorithmName.SHA3_256);
}
else
{
    // ...
}

SHA-3-ondersteuning is momenteel gericht op het ondersteunen van cryptografische primitieven. Constructies en protocollen op een hoger niveau worden in eerste instantie niet volledig ondersteund door SHA-3. Deze protocollen omvatten X.509-certificaten en SignedXmlCOSE.

Netwerken

Ondersteuning voor HTTPS-proxy

Tot nu toe hebben de proxytypen die alle toegestane HttpClient 'man-in-the-middle' ondersteund om te zien met welke site de client verbinding maakt, zelfs voor HTTPS-URI's. HttpClient ondersteunt nu HTTPS-proxy, waarmee een versleuteld kanaal wordt gemaakt tussen de client en de proxy, zodat alle aanvragen kunnen worden verwerkt met volledige privacy.

Als u https-proxy wilt inschakelen, stelt u de all_proxy omgevingsvariabele in of gebruikt u de WebProxy klasse om de proxy programmatisch te beheren.

Unix: export all_proxy=https://x.x.x.x:3218 Windows: set all_proxy=https://x.x.x.x:3218

U kunt de WebProxy klasse ook gebruiken om de proxy programmatisch te beheren.

ZipFile-methoden op basis van stream

.NET 8 bevat nieuwe overbelastingen waarmee ZipFile.CreateFromDirectory u alle bestanden die zijn opgenomen in een map kunt verzamelen en zippen, en vervolgens het resulterende zip-bestand kunt opslaan in de opgegeven stroom. Op dezelfde manier kunt u met nieuwe ZipFile.ExtractToDirectory overbelastingen een stroom met een gezipt bestand opgeven en de inhoud ervan extraheren in het bestandssysteem. Dit zijn de nieuwe overbelastingen:

namespace System.IO.Compression;

public static partial class ZipFile
{
    public static void CreateFromDirectory(
        string sourceDirectoryName, Stream destination);

    public static void CreateFromDirectory(
        string sourceDirectoryName,
        Stream destination,
        CompressionLevel compressionLevel,
        bool includeBaseDirectory);

    public static void CreateFromDirectory(
        string sourceDirectoryName,
        Stream destination,
        CompressionLevel compressionLevel,
        bool includeBaseDirectory,
    Encoding? entryNameEncoding);

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName) { }

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName, bool overwriteFiles) { }

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { }

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { }
}

Deze nieuwe API's kunnen handig zijn wanneer schijfruimte wordt beperkt, omdat ze voorkomen dat u de schijf als tussenliggende stap moet gebruiken.

Extensiebibliotheken

Deze sectie bevat de volgende subonderwerpen:

Sleutel-DI-services

Services voor sleutelafhankelijkheidsinjectie (DI) bieden een middel voor het registreren en ophalen van DI-services met behulp van sleutels. Met behulp van sleutels kunt u bepalen hoe u services registreert en verbruikt. Dit zijn enkele van de nieuwe API's:

In het volgende voorbeeld ziet u hoe u keyed DI-services gebruikt.

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<BigCacheConsumer>();
builder.Services.AddSingleton<SmallCacheConsumer>();
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
WebApplication app = builder.Build();
app.MapGet("/big", (BigCacheConsumer data) => data.GetData());
app.MapGet("/small", (SmallCacheConsumer data) => data.GetData());
app.MapGet("/big-cache", ([FromKeyedServices("big")] ICache cache) => cache.Get("data"));
app.MapGet("/small-cache", (HttpContext httpContext) => httpContext.RequestServices.GetRequiredKeyedService<ICache>("small").Get("data"));
app.Run();

class BigCacheConsumer([FromKeyedServices("big")] ICache cache)
{
    public object? GetData() => cache.Get("data");
}

class SmallCacheConsumer(IServiceProvider serviceProvider)
{
    public object? GetData() => serviceProvider.GetRequiredKeyedService<ICache>("small").Get("data");
}

public interface ICache
{
    object Get(string key);
}

public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

Zie dotnet/runtime#64427 voor meer informatie.

Gehoste levenscyclusservices

Gehoste services hebben nu meer opties voor uitvoering tijdens de levenscyclus van de toepassing. IHostedService geleverd StartAsync en StopAsync, en biedt nu IHostedLifecycleService deze aanvullende methoden:

Deze methoden worden respectievelijk vóór en na de bestaande punten uitgevoerd.

In het volgende voorbeeld ziet u hoe u de nieuwe API's gebruikt.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

internal class HostedLifecycleServices
{
    public async static void RunIt()
    {
        IHostBuilder hostBuilder = new HostBuilder();
        hostBuilder.ConfigureServices(services =>
        {
            services.AddHostedService<MyService>();
        });

        using (IHost host = hostBuilder.Build())
        {
            await host.StartAsync();
        }
    }

    public class MyService : IHostedLifecycleService
    {
        public Task StartingAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
        public Task StartAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
        public Task StartedAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
        public Task StopAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
        public Task StoppedAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
        public Task StoppingAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
    }
}

Zie dotnet/runtime#86511 voor meer informatie.

Validatie van opties

Brongenerator

Om de opstartoverhead te verminderen en de validatiefunctieset te verbeteren, hebben we een broncodegenerator geïntroduceerd waarmee de validatielogica wordt geïmplementeerd. De volgende code toont voorbeeldmodellen en validatieklassen.

public class FirstModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P1 { get; set; } = string.Empty;

    [Microsoft.Extensions.Options.ValidateObjectMembers(
        typeof(SecondValidatorNoNamespace))]
    public SecondModelNoNamespace? P2 { get; set; }
}

public class SecondModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P4 { get; set; } = string.Empty;
}

[OptionsValidator]
public partial class FirstValidatorNoNamespace
    : IValidateOptions<FirstModelNoNamespace>
{
}

[OptionsValidator]
public partial class SecondValidatorNoNamespace
    : IValidateOptions<SecondModelNoNamespace>
{
}

Als uw app gebruikmaakt van afhankelijkheidsinjectie, kunt u de validatie injecteren, zoals wordt weergegeven in de volgende voorbeeldcode.

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(
    builder.Configuration.GetSection("some string"));

builder.Services.AddSingleton<
    IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<
    IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>();

Type ValidateOptionsResultBuilder

.NET 8 introduceert het type om het ValidateOptionsResultBuilder maken van een ValidateOptionsResult object te vergemakkelijken. Belangrijk is dat deze opbouwfunctie de accumulatie van meerdere fouten toestaat. Voorheen was het maken van het ValidateOptionsResult object dat nodig is om te implementeren IValidateOptions<TOptions>.Validate(String, TOptions) moeilijk en leidde dit soms tot gelaagde validatiefouten. Als er meerdere fouten zijn opgetreden, stopt het validatieproces vaak bij de eerste fout.

In het volgende codefragment ziet u een voorbeeld van het gebruik van ValidateOptionsResultBuilder.

ValidateOptionsResultBuilder builder = new();
builder.AddError("Error: invalid operation code");
builder.AddResult(ValidateOptionsResult.Fail("Invalid request parameters"));
builder.AddError("Malformed link", "Url");

// Build ValidateOptionsResult object has accumulating multiple errors.
ValidateOptionsResult result = builder.Build();

// Reset the builder to allow using it in new validation operation.
builder.Clear();

LoggerMessageAttribute-constructors

LoggerMessageAttribute biedt nu extra overbelasting van constructors. Voorheen moest u de parameterloze constructor of de constructor kiezen die alle parameters vereist (gebeurtenis-id, logboekniveau en bericht). De nieuwe overbelastingen bieden meer flexibiliteit bij het opgeven van de vereiste parameters met verminderde code. Als u geen gebeurtenis-id opgeeft, genereert het systeem er automatisch een.

public LoggerMessageAttribute(LogLevel level, string message);
public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message);

Metrische gegevens voor extensies

IMeterFactory-interface

U kunt de nieuwe IMeterFactory interface registreren in afhankelijkheidsinjectiecontainers en deze gebruiken om objecten op een geïsoleerde manier te maken Meter .

Registreer de di-container IMeterFactory met behulp van de standaardmeterfactory-implementatie:

// 'services' is the DI IServiceCollection.
services.AddMetrics();

Consumenten kunnen vervolgens de meterfactory verkrijgen en gebruiken om een nieuw Meter object te maken.

IMeterFactory meterFactory = serviceProvider.GetRequiredService<IMeterFactory>();

MeterOptions options = new MeterOptions("MeterName")
{
    Version = "version",
};

Meter meter = meterFactory.Create(options);

Klasse MetricCollector<T>

Met de nieuwe MetricCollector<T> klasse kunt u metrische metingen opnemen, samen met tijdstempels. Daarnaast biedt de klasse de flexibiliteit om een tijdsprovider van uw keuze te gebruiken voor het genereren van nauwkeurige tijdstempels.

const string CounterName = "MyCounter";
DateTimeOffset now = DateTimeOffset.Now;

var timeProvider = new FakeTimeProvider(now);
using var meter = new Meter(Guid.NewGuid().ToString());
Counter<long> counter = meter.CreateCounter<long>(CounterName);
using var collector = new MetricCollector<long>(counter, timeProvider);

Assert.IsNull(collector.LastMeasurement);

counter.Add(3);

// Verify the update was recorded.
Assert.AreEqual(counter, collector.Instrument);
Assert.IsNotNull(collector.LastMeasurement);

Assert.AreSame(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement);
Assert.AreEqual(3, collector.LastMeasurement.Value);
Assert.AreEqual(now, collector.LastMeasurement.Timestamp);

System.Numerics.Tensors.TensorPrimitives

Het bijgewerkte NuGet-pakket System.Numerics.Tensors bevat API's in de nieuwe TensorPrimitives naamruimte die ondersteuning voor tensor-bewerkingen toevoegen. De tensor-primitieven optimaliseren gegevensintensieve workloads zoals die van AI en machine learning.

AI-workloads zoals semantische zoekopdrachten en het ophalen van augmented generation (RAG) breiden de mogelijkheden van natuurlijke taal van grote taalmodellen, zoals ChatGPT, uit door prompts met relevante gegevens uit te breiden. Voor deze workloads zijn bewerkingen op vectoren, zoals cosinus-gelijkenis , essentieel om de meest relevante gegevens te vinden om een vraag te beantwoorden. Het pakket System.Numerics.Tensors.TensorPrimitives biedt API's voor vectorbewerkingen, wat betekent dat u geen externe afhankelijkheid hoeft te nemen of uw eigen implementatie hoeft te schrijven.

Dit pakket vervangt het System.Numerics.Tensors-pakket.

Zie de blogpost Announcing .NET 8 RC 2 voor meer informatie.

Zie ook