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 ontrollen en automatisch vectoriseren met behulp van SIMD, zoals vergelijking, kopiëren en nulmaken, als het hun grootte tijdens het compileren kan bepalen.
Daarnaast is profielgeleide dynamische optimalisatie (PGO) verbeterd en is nu standaard ingeschakeld. U hoeft geen runtimeconfiguratieoptie meer te gebruiken om het mogelijk te maken. Dynamische PGO werkt nauw samen met gelaagde compilatie om code verder te optimaliseren, op basis van extra instrumentatie die wordt toegepast in fase 0.
Gemiddeld verhoogt dynamische PGO de prestaties met ongeveer 15%. In een benchmarksuite van ~4600 tests zag 23% prestatieverbeteringen van 20% of meer.
Codegen-struct promotie
.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 aggregatiesgenoemd) vervangt de velden van structvariabelen door primitieve variabelen die de JIT vervolgens nauwkeuriger kan redeneren en 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.
Vuilnisophaling
.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 afschaling te agressief gebeurt en er geen ruimte is voor de garbage collector om te manoeuvreren. In dit geval kunt u overwegen
GC.Collect(2, GCCollectionMode.Aggressive)
aan te roepen om het huidige geheugengebruik te verkleinen en probeer het vervolgens opnieuw. - 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 geven indien 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 harde heaplimiet die door de vernieuwing zal worden 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 zich aanmelden 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 ondersteunt alle landinstellingen ondersteund door mobiele.
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 eigenschap HybridGlobalization
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 Gedragsverschillenom 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 gebruiken om een interface als com-interface voor de brongenerator te markeren. 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 integreert met LibraryImportAttribute, en u kunt types met GeneratedComInterfaceAttribute als parameters en retourtypen gebruiken in LibraryImport
-geattribueerde 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 kenmerk GeneratedComClassAttribute waarmee u typen kunt doorgeven die interfaces met het kenmerk GeneratedComInterfaceAttribute implementeren voor onbeheerde 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 kenmerk GeneratedComInterfaceAttribute ondersteunen dezelfde typen als LibraryImportAttribute
en LibraryImportAttribute
ondersteunt nu GeneratedComInterface
-toegewezen typen en GeneratedComClass
-toegewezen typen.
Als uw C#-code alleen gebruikmaakt van een GeneratedComInterface
interface die is toegeschreven aan het verpakken van een COM-object uit niet-beheerde code of het verpakken van een beheerd object uit C# om onbeheerde code weer te geven, kunt u de opties in de eigenschap Options 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-omslagobjecten en de beheerde omslagobjecten te creëren en 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 het nieuwe type StrategyBasedComWrappers 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 het ComImportAttributebiedt een gloeilamp een optie om om te zetten naar bron-gegeneerd interop. De oplossing wijzigt de interface om het kenmerk GeneratedComInterfaceAttribute te gebruiken. Naast elke klasse die een interface met GeneratedComInterfaceAttribute
implementeert, biedt een gloeilamp een optie om het kenmerk GeneratedComClassAttribute toe te voegen aan het type. Zodra uw typen zijn geconverteerd, kunt u uw DllImport
methoden aanpassen om LibraryImportAttribute
te gebruiken.
Beperkingen
De COM-brongenerator biedt geen ondersteuning voor affiniteit met appartementen; het trefwoord new
gebruiken om een COM CoClass te activeren en de volgende API's:
- op IDispatchgebaseerde interfaces.
- op IInspectablegebaseerde interfaces.
- COM-eigenschappen en -gebeurtenissen.
Configuratiebindingsbrongenerator
.NET 8 introduceert een brongenerator om AOT- en trimvriendelijke -configuratie mogelijk te maken in ASP.NET Core. De generator is een alternatief voor de reeds bestaande op reflectie gebaseerde implementatie.
De brongenerator onderzoekt Configure(TOptions)-, Bind- en Get-oproepen om type-informatie 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 door AOT gecompileerde web-apps en wanneer PublishTrimmed
is ingesteld op true
(.NET 8+ apps). Voor andere projecttypen is de brongenerator standaard uitgeschakeld, maar u kunt ervoor kiezen om de eigenschap EnableConfigurationBindingGenerator
in te stellen op true
in uw projectbestand:
<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
- serialisatie
- Tijdabstractie
- UTF8-verbeteringen
- methoden voor het werken met willekeurigheid
- Prestatiegerichte typen
- System.Numerics en System.Runtime.Intrinsics
- gegevensvalidatie
- metrische gegevens
- Cryptografie
- Netwerken
- op Stream gebaseerde ZipFile-methoden
Reflectie
Functie-aanwijzers zijn geïntroduceerd in .NET 5, maar de bijbehorende ondersteuning voor weerspiegeling is op dat moment niet toegevoegd. Bij het gebruik van typeof
of reflectie op een functieaanwijzer, bijvoorbeeld typeof(delegate*<void>())
of FieldInfo.FieldType
, is 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 functieaanwijzerexemplaar, een fysiek adres voor een functie, wordt 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
Serialisatie
Er zijn veel verbeteringen aangebracht in System.Text.Json serialisatie- en deserialisatiefunctionaliteit in .NET 8. U kunt bijvoorbeeld de verwerking aanpassen van JSON-eigenschappen die niet bij de POCO-horen.
In de volgende secties worden andere serialisatieverbeteringen beschreven:
- ingebouwde ondersteuning voor extra typen
- brongenerator
- Interfacehiërarchieën
- Naamgevingsbeleid
- alleen-lezen eigenschappen
- standaardinstellingen voor weerspiegeling uitschakelen
- nieuwe JsonNode-API-methoden
- niet-openbare leden
- Streaming-deserialisatie-API's
- Extensiemethode WithAddedModifier
- nieuwe JsonContent.Create overloads
- een JsonSerializerOptions-exemplaar blokkeren
Zie JSON-serialisatie en deserialisatie in .NETvoor 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]
waarden voor Memory<T> en ReadOnlyMemory<T>.
byte
waarden worden geserialiseerd naar Base64-tekenreeksen en andere typen naar JSON-matrices.JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 }); // "AQID" JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); // [1,2,3]
Broncodegenerator
.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. Bijvoorbeeld:
De brongenerator ondersteunt nu serialisatietypen met
required
eninit
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 het nesten van
JsonSerializerContext
-verklaringen binnen willekeurige typecategorieën.Ondersteuning voor door compiler gegenereerde of onuitkenbare typen in zwak getypeerde brongeneratiescenario's. Omdat door de compiler gegenereerde typen niet expliciet kunnen worden opgegeven door de brongenerator, voert System.Text.Json nu tijdens uitvoertijd de oplossing van de dichtstbijzijnde voorouder uit. Deze resolutie bepaalt het meest geschikte supertype waarmee de waarde moet worden geserialiseerd.
Nieuw type converter
JsonStringEnumConverter<TEnum>
. 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 { }
Voor meer informatie, zie Enum-velden serialiseren als tekenreeksen.
Met de nieuwe
JsonConverter.Type
-eigenschap kunt u het type van een niet-generiekJsonConverter
-exemplaar opzoeken:Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters) => converters.Where(converter => converter.Type != null) .ToDictionary(converter => converter.Type!);
De eigenschap is nulwaarde omdat deze
null
retourneert voorJsonConverterFactory
instanties entypeof(T)
voorJsonConverter<T>
instanties.
Ketenbron generatoren
De JsonSerializerOptions klasse bevat een nieuwe TypeInfoResolverChain eigenschap die een aanvulling vormt op de bestaande TypeInfoResolver eigenschap. Deze eigenschappen worden gebruikt bij het aanpassen van contracten om brongeneratoren te koppelen. De toevoeging van de nieuwe eigenschap betekent dat u niet alle gekoppelde onderdelen bij één keer aanroepen hoeft op te geven—ze kunnen achteraf worden toegevoegd. Met TypeInfoResolverChain kunt u de keten ook inspecteren of onderdelen ervan verwijderen. Zie Brongeneratoren combinerenvoor meer informatie.
Bovendien is JsonSerializerOptions.AddContext<TContext>() nu verouderd. Het is vervangen door de TypeInfoResolver en TypeInfoResolverChain eigenschappen. Zie SYSLIB0049voor 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 de conversie van de eigenschapsnamen snake_case
(met een onderstrepingsteken) en kebab-case
(met een afbreekstreepje). 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 gebruikenvoor meer informatie.
Alleen-lezen eigenschappen
U kunt nu deserialiseren naar alleen-lezen velden of eigenschappen (dat wil gezegd, velden die geen set
accessor hebben).
Als u zich wereldwijd wilt aanmelden voor deze ondersteuning, stelt u een nieuwe optie, PreferredObjectCreationHandling, in op JsonObjectCreationHandling.Populate. Als compatibiliteit een probleem is, kunt u de functionaliteit ook gedetailleerder inschakelen door het kenmerk [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
te plaatsen op specifieke typen waarvan de eigenschappen moeten worden ingevuld, of op afzonderlijke eigenschappen.
Denk bijvoorbeeld aan de volgende code die wordt gedeserialiseerd naar een CustomerInfo
-type met twee alleen-leesbare 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 de eigenschappen Names
en Company
de standaardwaarden behouden.
{"Names":[],"Company":{"Name":"N/A","PhoneNumber":"N/A"}}
De invoerwaarden worden nu gebruikt om de alleen-lezen eigenschappen te vullen tijdens de deserialisatie.
{"Names":["John Doe"],"Company":{"Name":"Contoso","PhoneNumber":"N/A"}}
Zie Geïnitialiseerde eigenschappenvullen voor meer informatie over het deserialisatiegedrag.
Standaardinstelling voor weerspiegeling uitschakelen
U kunt nu standaard het gebruik van de op reflectie gebaseerde serializer uitschakelen. Deze uitschakeling is nuttig om te voorkomen dat reflectiecomponenten onbedoeld worden geworteld die niet eens in gebruik zijn, vooral in afgeslankte en native 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 eigenschap JsonSerializerIsReflectionEnabledByDefault
MSBuild in op false
in uw projectbestand.
Gebruik de nieuwe IsReflectionEnabledByDefault-API om de waarde van de functieswitch te controleren. Als u een bibliotheekauteur bent die boven op System.Text.Jsonbouwt, kunt u vertrouwen op de eigenschap om uw standaardinstellingen te configureren zonder per ongeluk weerspiegelingsonderdelen te rooten.
Zie Standaardinstellingen voor weerspiegeling uitschakelenvoor meer informatie.
Nieuwe JsonNode-API-methoden
De typen JsonNode en System.Text.Json.Nodes.JsonArray 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 opnemen in het serialisatiecontract voor een bepaald type met behulp van JsonIncludeAttribute en JsonConstructorAttribute attribuutannotaties.
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 gebruikenvoor meer informatie.
Streaming-deserialisatie-API's
.NET 8 bevat nieuwe IAsyncEnumerable<T> uitbreidingsmethoden voor streamingdeserialisatie, bijvoorbeeld GetFromJsonAsAsyncEnumerable. Er bestaan vergelijkbare methoden die Task<TResult>retourneren, bijvoorbeeld HttpClientJsonExtensions.GetFromJsonAsync. 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-overbelastingen
U kunt nu JsonContent exemplaren maken met trim-safe of door bron gegenereerde contracten. De nieuwe methoden zijn:
- JsonContent.Create(Object, JsonTypeInfo, MediaTypeHeaderValue)
- JsonContent.Create<T>(T, JsonTypeInfo<T>, MediaTypeHeaderValue)
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 bevriezen
Met de volgende nieuwe methoden kunt u bepalen wanneer een JsonSerializerOptions exemplaar wordt bevroren.
JsonSerializerOptions.MakeReadOnly()
Deze overbelasting is ontworpen om veilig met memory- of resource-trimming om te gaan en zal daarom een uitzondering genereren in gevallen waarin de opties-instantie niet is geconfigureerd met een resolver.
JsonSerializerOptions.MakeReadOnly(Boolean)
Als u
true
aan deze overload doorgeeft, wordt het exemplaar van de opties gevuld met de standaardreflectieresolver indien er een ontbreekt. Deze methode is gemarkeerd alsRequiresUnreferenceCode
/RequiresDynamicCode
en is daarom niet geschikt voor systeemeigen AOT-toepassingen.
Met de nieuwe eigenschap IsReadOnly kunt u controleren of de optie-instantie is bevroren.
Tijdabstractie
Met de nieuwe TimeProvider-klasse en ITimer-interface kunt u tijdabstractie functionaliteit toevoegen, zodat u de tijd in testscenario's kunt mocken. Daarnaast kunt u de tijdabstractie gebruiken om Task bewerkingen te mocken die afhankelijk zijn van de voortgang van de tijd met behulp van Task.Delay en Task.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 naar een doelbereik wilt inschakelen, implementeert u de nieuwe IUtf8SpanFormattable interface op uw type. Deze nieuwe interface is nauw verwant aan ISpanFormattable, maar richt zich 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 om string
, Span<char>
of Span<byte>
. Het heeft volledige ondersteuning voor alle indelingen (met inbegrip van de nieuwe "B" binaire aanduiding) en alle culturen. Dit betekent dat u nu rechtstreeks naar UTF8 kunt opmaken vanuit Byte
, Complex
, Char
, DateOnly
, DateTime
, DateTimeOffset
, Decimal
, Double
, Guid
, Half
, IPAddress
, IPNetwork
, Int16
, Int32
, Int64
, Int128
, IntPtr
, NFloat
, SByte
, Single
, Rune
, TimeOnly
, TimeSpan
, UInt16
, UInt32
, UInt64
, UInt128
, UIntPtr
en Version
.
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 in de waardeformaten en gebruikt hun implementaties om hun UTF8-representaties rechtstreeks naar het doelgebied 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 de methoden false
in plaats van een uitzondering te genereren.
Methoden voor het werken met willekeurigheid
De typen System.Random en System.Security.Cryptography.RandomNumberGenerator introduceren twee nieuwe methoden voor het werken met willekeurigheid.
GetItems<T>()
Met de nieuwe System.Random.GetItems- en System.Security.Cryptography.RandomNumberGenerator.GetItems-methoden kunt u willekeurig een opgegeven aantal items uit een invoerset kiezen. In het volgende voorbeeld ziet u hoe u System.Random.GetItems<T>()
(op het exemplaar van de eigenschap Random.Shared) gebruikt om willekeurig 31 items in een matrix in te voegen. 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- en RandomNumberGenerator.Shuffle<T>(Span<T>)-methoden kunt u de volgorde van een bereik willekeurig maken. Deze methoden zijn nuttig voor het verminderen van vooroordelen in training binnen machine learning (dus de eerste stap is niet altijd trainen en de laatste stap niet altijd testen).
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(); // ... if (s_configurationData.TryGetValue(key, out bool setting) && setting) { Process(); }
Methoden zoals MemoryExtensions.IndexOfAny zoeken naar het eerste exemplaar van een willekeurige waarde in de doorgegeven verzameling. Het nieuwe type System.Buffers.SearchValues<T> 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 van SearchValues<T>maakt, worden alle gegevens die nodig zijn om volgende zoekopdrachten te optimaliseren, afgeleid op dat moment, 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). Er wordt vooraf een beetje extra tijd besteed aan het uitvoeren van werk zoals het parseren van de tekenreeks, maar dit bespaart het werk bij elk gebruik.
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 typen System.IO.Hashing.XxHash3 en System.IO.Hashing.XxHash128 bieden implementaties van de snelle XXH3- en XXH128-hashalgoritmen.
System.Numerics en System.Runtime.Intrinsics
Deze sectie bevat verbeteringen in de System.Numerics en System.Runtime.Intrinsics naamruimten.
-
Vector256<T>, Matrix3x2en Matrix4x4 hebben de hardwareversnelling op .NET 8 verbeterd. Waar mogelijk is bijvoorbeeld Vector256<T> intern gewijzigd naar
2x Vector128<T>
-bewerkingen. Dit maakt gedeeltelijke versnelling van bepaalde functies mogelijk wanneer zowelVector128.IsHardwareAccelerated == true
alsVector256.IsHardwareAccelerated == false
, zoals op Arm64. - Hardware-intrinsieke kenmerken worden nu geannoteerd met het kenmerk
ConstExpected
. 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 aanfloat
(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
.NET Core 3.0 breidde de SIMD-ondersteuning uit om de platformspecifieke hardware-intrinsieke API's voor x86/x64 op te nemen. .NET 5 heeft ondersteuning toegevoegd voor Arm64 en .NET 7 heeft ondersteuning toegevoegd voor platformoverschrijdende hardware-intrinsieken. .NET 8 verbetert de ondersteuning voor SIMD door de introductie van Vector512<T> en de ondersteuning voor Intel Advanced Vector Extensions 512 (AVX-512) instructies.
In het bijzonder bevat .NET 8 ondersteuning voor de volgende belangrijke functies van AVX-512:
- 512-bit vectorbewerkingen
- 16 extra 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 Vector512.IsHardwareAccelerated nu true
.
.NET 8 voegt ook verschillende platformspecifieke klassen toe onder de System.Runtime.Intrinsics.X86 naamruimte:
- Avx512F (basis)
- Avx512BW (byte en woord)
- Avx512CD (conflictdetectie)
- Avx512DQ (dubbelwoord en kwadword)
- Avx512Vbmi (instructies voor manipulatie van vector byte)
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
(vectorlengte)-extensies voor de bijbehorende instructieset worden weergegeven.
Zelfs als u niet expliciet Vector512
-specifieke of Avx512F
-specifieke instructies in uw code gebruikt, profiteert u waarschijnlijk nog steeds van de nieuwe AVX-512-ondersteuning. De JIT kan impliciet profiteren van de extra registers en instructies bij het gebruik van Vector128<T> of Vector256<T>. De basisklassebibliotheek gebruikt deze hardwareintrinsieken intern in de meeste bewerkingen die worden weergegeven door Span<T> en ReadOnlySpan<T> en 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 cloudservices. Hoewel de bestaande DataAnnotations
validators zijn afgestemd op typische validatie van gegevensinvoer in de gebruikersinterface, 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 het RangeAttribute type.
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.
[Length(10, 20)] vereist bijvoorbeeld 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")] . |
Statistieken
Met nieuwe API's kunt u tags voor sleutel-waardepaar koppelen aan Meter 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:
- MeterOptions
- Meter(MeterOptions)
- CreateCounter<T>(String, String, String, IEnumerable<KeyValuePair<String,Object>>)
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 omvat SHA3_256
, SHA3_384
en SHA3_512
voor hashing; HMACSHA3_256
, HMACSHA3_384
en HMACSHA3_512
voor HMAC; HashAlgorithmName.SHA3_256
, HashAlgorithmName.SHA3_384
en HashAlgorithmName.SHA3_512
voor hashing waar het algoritme kan worden geconfigureerd; en RSAEncryptionPadding.OaepSHA3_256
, RSAEncryptionPadding.OaepSHA3_384
en RSAEncryptionPadding.OaepSHA3_512
voor RSA OAEP-versleuteling.
In het volgende voorbeeld ziet u hoe u de API's gebruikt, inclusief de eigenschap SHA3_256.IsSupported
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, SignedXmlen COSE.
Netwerken
Ondersteuning voor HTTPS-proxy
Tot nu toe lieten de door HttpClient ondersteunde proxytypen allemaal een 'man-in-the-middle-aanval' zien met welke site de client verbonden is, zelfs voor HTTPS-URI's. HttpClient ondersteunt nu HTTPS-proxy, waardoor een versleuteld kanaal tussen de client en de proxy wordt gemaakt, zodat alle aanvragen met volledige privacy kunnen worden verwerkt.
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 van ZipFile.CreateFromDirectory waarmee u alle bestanden in een map kunt verzamelen en deze kunt zippen en het resulterende zip-bestand vervolgens kunt opslaan in de opgegeven stroom. Op dezelfde manier kunt u met nieuwe ZipFile.ExtractToDirectory overloads een stream met een zip-bestand opgeven en de inhoud ervan in het bestandssysteem extraheren. 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:
- validatie van opties
- LoggerMessageAttribute-constructors
- Extensie metrieken
- gehoste levenscyclusservices
- Gesleutelde DI-diensten
- System.Numerics.Tensors.TensorPrimitives
Gesleutelde DI-diensten
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:
- De IKeyedServiceProvider interface.
- Het kenmerk ServiceKeyAttribute, dat kan worden gebruikt om de sleutel te injecteren die is gebruikt voor registratie/resolutie in de constructor.
- Het kenmerk FromKeyedServicesAttribute, dat kan worden gebruikt voor serviceconstructorparameters om op te geven welke service met sleutel moet worden gebruikt.
- Verschillende nieuwe uitbreidingsmethoden voor IServiceCollection ter ondersteuning van sleutelservices, bijvoorbeeld ServiceCollectionServiceExtensions.AddKeyedScoped.
- De ServiceProvider tenuitvoerlegging van IKeyedServiceProvider.
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#64427voor meer informatie.
Gehoste levenscyclusservices
Gehoste services hebben nu meer opties voor uitvoering tijdens de levenscyclus van de toepassing.
IHostedService heeft StartAsync
en StopAsync
geleverd, en IHostedLifecycleService biedt nu deze aanvullende methoden:
- StartingAsync(CancellationToken)
- StartedAsync(CancellationToken)
- StoppingAsync(CancellationToken)
- StoppedAsync(CancellationToken)
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#86511voor meer informatie.
Validatie van opties
Broncodegenerator
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 van ValidateOptionsResultBuilder
.NET 8 introduceert het ValidateOptionsResultBuilder type om het 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 voor het implementeren van IValidateOptions<TOptions>.Validate(String, TOptions) moeilijk en soms resulteerde in 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();
Constructeurs van LoggerMessageAttribute
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 op een geïsoleerde manier Meter objecten te maken.
Registreer de IMeterFactory bij de DI-container met behulp van de standaardmeterfactory-implementatie:
// 'services' is the DI IServiceCollection.
services.AddMetrics();
Consumenten kunnen vervolgens de meterfabriek verkrijgen en deze 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 System.Numerics.Tensors NuGet-pakket bevat API's in het nieuwe System.Numerics.Tensors.TensorPrimitives type waarmee ondersteuning voor tensor-bewerkingen wordt toegevoegd. De tensor-primitieven optimaliseren gegevensintensieve workloads zoals die van AI en machine learning.
AI-workloads zoals semantische zoekopdrachten en retrieval-augmented generation (RAG) breiden de natuurlijke taalvaardigheden van grote taalmodellen, zoals ChatGPT, uit door prompts aan te vullen met relevante gegevens. Voor deze workloads zijn bewerkingen op vectoren, zoals cosinus-gelijkenis om de meest relevante gegevens te vinden om een vraag te beantwoorden, cruciaal. Het TensorPrimitives type biedt API's voor vectorbewerkingen.
Zie de blogpost Aankondiging van .NET 8 RC 2voor meer informatie.
Systeemeigen AOT-ondersteuning
De optie voor publiceren als systeemeigen AOT- is voor het eerst geïntroduceerd in .NET 7. Als u een app publiceert met Native AOT, wordt er een volledig zelfstandige versie van uw app gemaakt die geen runtime nodig heeft. Alles is opgenomen in één bestand. .NET 8 brengt de volgende verbeteringen aan in Native AOT-publicatie:
Voegt ondersteuning toe voor de x64- en Arm64-architecturen op macOS-.
Vermindert de groottes van native AOT-apps op Linux met maximaal 50%. In de volgende tabel ziet u de grootte van een 'Hallo wereld'-app die is gepubliceerd met Native AOT, met daarin de volledige .NET-runtime op .NET 7 versus .NET 8:
Besturingssysteem .NET 7 .NET 8 Linux x64 (met -p:StripSymbols=true
)3,76 MB 1,84 MB Windows x64 2,85 MB 1,77 MB Hiermee kunt u een optimalisatievoorkeur opgeven: grootte of snelheid. Standaard kiest de compiler ervoor om snelle code te genereren terwijl u rekening houdt met de grootte van de toepassing. U kunt echter de eigenschap
<OptimizationPreference>
MSBuild gebruiken om specifiek voor de ene of de andere te optimaliseren. Zie AOT-implementaties optimaliserenvoor meer informatie.
IOS-achtige platforms targeten met systeemeigen AOT
.NET 8 start het werk om systeemeigen AOT-ondersteuning in te schakelen voor iOS-achtige platforms. U kunt nu .NET iOS- en .NET SDK-toepassingen bouwen en uitvoeren met Native AOT op de volgende platforms:
ios
iossimulator
maccatalyst
tvos
tvossimulator
Voorlopige tests tonen aan dat de app-grootte op schijf met ongeveer 35% voor .NET iOS-apps die systeemeigen AOT gebruiken in plaats van Mono, afneemt. De app-grootte op schijf voor .NET MAUI iOS-apps kan tot 50%kleiner worden. Daarnaast is de opstarttijd ook sneller. .NET iOS-apps hebben ongeveer 28% snellere opstarttijd, terwijl .NET MAUI iOS-apps ongeveer 50% betere opstartprestaties hebben in vergelijking met Mono. De .NET 8-ondersteuning is experimenteel en alleen de eerste stap voor de functie als geheel. Zie de blogpost .NET 8 Performance Improvements in .NET MAUIvoor meer informatie.
Systeemeigen AOT-ondersteuning is beschikbaar als een opt-in-functie die is bedoeld voor app-implementatie; Mono is nog steeds de standaardruntime voor het ontwikkelen en implementeren van apps. Als u een .NETLOAD-toepassing met Native AOT wilt bouwen en uitvoeren op een iOS-apparaat, gebruikt u dotnet workload install maui
om de .NETLOAD-workload te installeren en dotnet new maui -n HelloMaui
om de app te maken. Stel vervolgens de eigenschap MSBuild PublishAot
in op true
in het projectbestand.
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
Wanneer u de vereiste eigenschap instelt en dotnet publish
uitvoert, zoals wordt weergegeven in het volgende voorbeeld, wordt de app geïmplementeerd met behulp van systeemeigen AOT.
dotnet publish -f net8.0-ios -c Release -r ios-arm64 /t:Run
Beperkingen
Niet alle iOS-functies zijn compatibel met Systeemeigen AOT. Op dezelfde manier zijn niet alle bibliotheken die vaak worden gebruikt in iOS compatibel met NativeAOT. Naast de bestaande beperkingen van systeemeigen AOT-uitrol, worden in de volgende lijst enkele van de andere beperkingen weergegeven bij het richten op iOS-achtige platforms.
- Systeemeigen AOT gebruiken is alleen ingeschakeld tijdens app-implementatie (
dotnet publish
). - Foutopsporing van beheerde code wordt alleen ondersteund met Mono.
- De compatibiliteit met het .NET MAUI-framework is beperkt.
AOT-compilatie voor Android-apps
Om de grootte van apps te verkleinen, gebruiken .NET- en .NET MAUI-apps die gericht zijn op Android de geprofileerde ahead-of-time (AOT) compilatiemodus wanneer ze zijn ingebouwd in de Release-modus. Geprofileerde AOT-compilatie beïnvloedt minder methoden dan reguliere AOT-compilatie. .NET 8 introduceert de eigenschap <AndroidStripILAfterAOT>
waarmee u zich kunt aanmelden voor verdere AOT-compilatie voor Android-apps om de app-grootte nog verder te verkleinen.
<PropertyGroup>
<AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
</PropertyGroup>
Het instellen van AndroidStripILAfterAOT
op true
overschrijft standaard de standaardinstelling AndroidEnableProfiledAot
, waardoor (bijna) alle methoden die door AOT zijn gecompileerd, worden ingekort. U kunt ook geprofileerde AOT- en IL-stripping tegelijkertijd gebruiken door beide eigenschappen expliciet in te stellen op true
:
<PropertyGroup>
<AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
Ingebouwde Windows-apps
Wanneer u apps bouwt die gericht zijn op Windows op niet-Windows-platforms, wordt het resulterende uitvoerbare bestand nu bijgewerkt met opgegeven Win32-resources, bijvoorbeeld toepassingspictogram, manifest, versie-informatie.
Voorheen moesten toepassingen worden gebouwd op Windows om dergelijke resources te kunnen hebben. Het oplossen van deze kloof in ondersteuning voor meerdere gebouwen is een populaire aanvraag geweest, omdat het een belangrijk pijnpunt was dat zowel de complexiteit van de infrastructuur als het resourcegebruik beïnvloedde.