Novinky v modulu runtime .NET 8

Tento článek popisuje nové funkce modulu runtime .NET pro .NET 8.

Zlepšení výkonu

.NET 8 zahrnuje vylepšení generování kódu a kompilace JIT (just-in time):

  • Vylepšení výkonu Arm64
  • Vylepšení SIMD
  • Podpora rozšíření AVX-512 ISA (viz Vector512 a AVX-512)
  • Vylepšení nativní pro cloud
  • Vylepšení propustnosti JIT
  • Smyčka a obecné optimalizace
  • Optimalizovaný přístup pro pole označená pomocí ThreadStaticAttribute
  • Po sobě jdoucí přidělení registru. Arm64 má dva instrukce pro vyhledávání vektorů tabulky, které vyžadují, aby všechny entity v jejich operandech řazené kolekce členů byly přítomny v po sobě jdoucích registrech.
  • JIT/NativeAOT teď může zrušit registraci a automaticky vektorizovat některé operace paměti pomocí SIMD, jako je porovnání, kopírování a nulace, pokud dokáže určit jejich velikosti v době kompilace.

Kromě toho se vylepšila dynamická optimalizace s asistencí profilu (PGO) a je teď ve výchozím nastavení povolená. K jeho povolení už nemusíte používat možnost konfigurace modulu runtime. Dynamické PGO funguje ručně s vrstvenou kompilací pro další optimalizaci kódu na základě další instrumentace, která je zavedená během vrstvy 0.

Dynamické PGO v průměru zvyšuje výkon o přibližně 15 %. V srovnávací sadě testů ~4600 zaznamenalo 23 % zlepšení výkonu o 20 % nebo více.

Zvýšení úrovně struktury Codegen

.NET 8 obsahuje nový průchod optimalizace fyzické povýšení pro codegen, který generalizuje schopnost JIT zvýšit úroveň proměnných struktury. Tato optimalizace (označovaná také jako skalární nahrazení agregací) nahrazuje pole proměnných struktury primitivními proměnnými, které jiT pak dokáže zdůvodnit a optimalizovat přesněji.

JIT už tuto optimalizaci podporuje, ale s několika velkými omezeními, mezi které patří:

  • Byla podporována pouze pro struktury se čtyřmi nebo méně poli.
  • Podporovalo se pouze v případě, že každé pole bylo primitivním typem nebo jednoduchou strukturou, která obtéká primitivní typ.

Fyzické povýšení odebere tato omezení, která řeší řadu dlouhodobých problémů s JIT.

Uvolnění paměti

.NET 8 přidává funkci pro úpravu limitu paměti za běhu. To je užitečné ve scénářích cloudových služeb, ve kterých přichází a přechází poptávka. Aby byly nákladově efektivní, měly by služby vertikálně navýšit a snížit spotřebu prostředků, protože poptávka kolísá. Když služba zjistí snížení poptávky, může snížit spotřebu prostředků snížením kapacity snížením limitu paměti. Dříve to se nezdařilo, protože uvolňování paměti (GC) nevěděl o změně a mohlo by přidělit více paměti než nový limit. Při této změně můžete volat RefreshMemoryLimit() rozhraní API, které aktualizuje GC s novým limitem paměti.

Existuje několik omezení, o nichž je potřeba vědět:

  • Na 32bitových platformách (například Windows x86 a Linux ARM) nemůže .NET navázat nový pevný limit haldy, pokud ještě neexistuje.
  • Rozhraní API může vrátit nenulový stavový kód označující selhání aktualizace. K tomu může dojít, pokud je vertikální snížení kapacity příliš agresivní a neopustí prostor pro manévrování GC. V takovém případě zvažte volání GC.Collect(2, GCCollectionMode.Aggressive) pro zmenšení aktuálního využití paměti a akci opakujte.
  • Pokud vertikálně navýšit kapacitu limitu paměti nad rámec velikosti, o které se GC domnívá, že proces dokáže zpracovat během spouštění, volání bude úspěšné, RefreshMemoryLimit ale nebude moct použít více paměti, než to, co považuje za limit.

Následující fragment kódu ukazuje, jak volat rozhraní API.

GC.RefreshMemoryLimit();

Můžete také aktualizovat některá nastavení konfigurace GC související s limitem paměti. Následující fragment kódu nastaví pevný limit haldy na 100 mebibajtů (MiB):

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

Rozhraní API může vyvolat InvalidOperationException výjimku, pokud je pevný limit neplatný, například v případě záporných procent pevného limitu haldy a pokud je pevný limit příliš nízký. K tomu může dojít v případě, že pevný limit haldy, který aktualizace nastaví, ať už kvůli novým nastavením AppData nebo změnám limitu paměti kontejneru, je nižší než hodnota, která je již potvrzena.

Globalizace pro mobilní aplikace

Mobilní aplikace v systémech iOS, tvOS a MacCatalyst se můžou rozhodnout pro nový režim hybridní globalizace, který používá světlejší sadu ICU. V hybridním režimu se data globalizace částečně načítá ze sady ICU a částečně z volání do nativních rozhraní API. Hybridní režim obsluhuje všechna národní prostředí podporovaná mobilním zařízením.

Hybridní režim je nejvhodnější pro aplikace, které nemůžou pracovat v neutrálním režimu globalizace a používají jazykové verze, které byly na mobilních zařízeních oříznuté z dat ICU. Můžete ho také použít, když chcete načíst menší datový soubor ICU. (Soubor icudt_hybrid.dat je o 34,5 % menší než výchozí datový soubor ICU icudt.dat.)

Chcete-li použít režim hybridní globalizace, nastavte HybridGlobalization vlastnost MSBuild na true:

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

Existuje několik omezení, o nichž je potřeba vědět:

  • Vzhledem k omezením nativního rozhraní API nejsou v hybridním režimu podporována všechna rozhraní API globalizace.
  • Některá podporovaná rozhraní API mají jiné chování.

Pokud chcete zkontrolovat, jestli je aplikace ovlivněná, podívejte se na rozdíly v chování.

Zprostředkovatele komunikace modelu COM generovaného zdrojem

.NET 8 obsahuje nový generátor zdrojů, který podporuje spolupráci s rozhraními MODELU COM. Můžete použít GeneratedComInterfaceAttribute k označení rozhraní jako rozhraní MODELU COM pro zdrojový generátor. Generátor zdrojů pak vygeneruje kód, který povolí volání z kódu jazyka C# do nespravovaného kódu. Také vygeneruje kód, který povolí volání z nespravovaného kódu do C#. Tento zdrojový generátor se integruje s LibraryImportAttributea můžete použít typy s GeneratedComInterfaceAttribute parametry a návratové typy v LibraryImport-attributed metod.

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

Generátor zdrojového kódu také podporuje nový GeneratedComClassAttribute atribut, který umožňuje předat typy, které implementují rozhraní s atributem GeneratedComInterfaceAttribute do nespravovaného kódu. Zdrojový generátor vygeneruje kód potřebný k vystavení objektu COM, který implementuje rozhraní a předává volání spravované implementaci.

Metody v rozhraních s atributem GeneratedComInterfaceAttribute podporují všechny stejné typy jako LibraryImportAttributea LibraryImportAttribute nyní podporuje GeneratedComInterface-attributed typy a GeneratedComClass-attributed typy.

Pokud kód jazyka C# používá GeneratedComInterfacepouze rozhraní -attributed k zabalení objektu COM z nespravovaného kódu nebo zabalení spravovaného objektu z jazyka C# k vystavení nespravovanému kódu, můžete pomocí možností ve Options vlastnosti přizpůsobit, který kód se vygeneruje. Tyto možnosti znamenají, že nemusíte psát marshallery pro scénáře, o kterých víte, že se nebudou používat.

Generátor zdrojů používá nový StrategyBasedComWrappers typ k vytvoření a správě obálky objektů MODELU COM a obálky spravovaných objektů. Tento nový typ zpracovává poskytování očekávaného uživatelského prostředí .NET pro interoperabilitu modelu COM a poskytuje body přizpůsobení pro pokročilé uživatele. Pokud má vaše aplikace vlastní mechanismus pro definování typů z modelu COM nebo pokud potřebujete podporovat scénáře, které zdrojové generované com v současné době nepodporují, zvažte použití nového StrategyBasedComWrappers typu k přidání chybějících funkcí pro váš scénář a získání stejného uživatelského prostředí .NET pro vaše typy modelu COM.

Pokud používáte Visual Studio, nové analyzátory a opravy kódu usnadňují převod existujícího kódu zprostředkovatele komunikace modelu COM tak, aby používaly zdrojově vygenerovanou interoperabilitu. Vedle každého rozhraní, které má žárovku ComImportAttribute, nabízí možnost převést na zdroj generovanou interoperabilitu. Oprava změní rozhraní tak, aby používalo GeneratedComInterfaceAttribute atribut. A vedle každé třídy, která implementuje rozhraní s GeneratedComInterfaceAttribute, žárovka nabízí možnost přidat GeneratedComClassAttribute atribut k typu. Po převodu typů můžete přesunout metody DllImport , které chcete použít LibraryImportAttribute.

Omezení

Zdrojový generátor modelu COM nepodporuje spřažení bytu, pomocí klíčového new slova aktivuje třídu COM CoClass a následující rozhraní API:

  • IDispatchrozhraní založená na rozhraních.
  • IInspectablerozhraní založená na rozhraních.
  • Vlastnosti a události modelu COM.

Generátor zdrojů vazby konfigurace

.NET 8 zavádí zdrojový generátor, který poskytuje AOT a konfiguraci přívětivou pro střih v ASP.NET Core. Generátor je alternativou k existující implementaci založené na reflexi.

Sondy generátoru zdroje pro Configure(TOptions), Binda Get volání pro načtení informací o typu z. Pokud je generátor povolen v projektu, kompilátor implicitně zvolí generované metody nad předem existujícími implementacemi architektury založené na reflexi.

K použití generátoru nejsou potřeba žádné změny zdrojového kódu. Ve výchozím nastavení je povolená ve webových aplikacích AOT. U jiných typů projektů je zdrojový generátor ve výchozím nastavení vypnutý, ale můžete se přihlásit nastavením EnableConfigurationBindingGenerator vlastnosti do true souboru projektu:

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

Následující kód ukazuje příklad vyvolání pořadače.

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

Knihovny Core .NET

Tato část obsahuje následující dílčí témata:

Reflexe

Ukazatele funkcí byly zavedeny v .NET 5, ale odpovídající podpora reflexe nebyla v té době přidána. Při použití typeof nebo odrazu na ukazateli funkce, například typeof(delegate*<void>()) nebo FieldInfo.FieldType v uvedeném pořadí, IntPtr byl vrácen. Počínaje rozhraním System.Type .NET 8 se místo toho vrátí objekt. Tento typ poskytuje přístup k metadatům ukazatelů funkce, včetně konvencí volání, návratového typu a parametrů.

Poznámka:

Instance ukazatele funkce, která je fyzickou adresou funkce, je nadále reprezentována jako .IntPtr Změnil se pouze typ odrazu.

Nové funkce jsou v současné době implementovány pouze v modulu runtime CoreCLR a MetadataLoadContext.

Nová rozhraní API byla přidána do System.Type, například IsFunctionPointerdo , do a System.Reflection.FieldInfodo System.Reflection.PropertyInfo, a System.Reflection.ParameterInfo. Následující kód ukazuje, jak použít některá nová rozhraní API k reflexi.

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

Předchozí příklad vytvoří následující výstup:

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

Serializace

V rozhraní .NET 8 bylo provedeno System.Text.Json mnoho vylepšení serializace a deserializace. Můžete například přizpůsobit zpracování členů, které nejsou v datové části JSON.

Následující části popisují další vylepšení serializace:

Další informace o serializaci JSON obecně naleznete v tématu Serializace a deserializace JSON v .NET.

Integrovaná podpora dalších typů

Serializátor má integrovanou podporu pro následující další typy.

  • Half, Int128a UInt128 číselné typy.

    Console.WriteLine(JsonSerializer.Serialize(
        [ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ]
    ));
    // [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]
    
  • Memory<T> a ReadOnlyMemory<T> hodnoty. byte hodnoty jsou serializovány na řetězce Base64 a další typy do polí JSON.

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

Generátor zdrojů

.NET 8 zahrnuje vylepšení zdrojového generátoru System.Text.Json, které jsou zaměřeny na to, aby nativní prostředí AOT bylo na stejné úrovni jako serializátor založený na reflexi. Příklad:

  • Generátor zdrojů teď podporuje serializaci typů s vlastnostmi a init jejich vlastnostmirequired. Oba byly již podporovány v serializaci založené na reflexi.

  • Vylepšené formátování zdrojového kódu

  • JsonSourceGenerationOptionsAttribute parita funkcí s JsonSerializerOptions. Další informace naleznete v tématu Určení možností (generování zdroje).

  • Další diagnostika (například SYSLIB1034 a SYSLIB1039).

  • Nezahrnujte typy ignorovaných nebo nepřístupných vlastností.

  • Podpora vnořování deklarací JsonSerializerContext v rámci libovolného typu

  • Podpora vygenerovaných nebo nepotřebných typů kompilátoru ve scénářích generování zdroje se slabým typem Vzhledem k tomu, že generátor zdrojů explicitně generované typy kompilátoru není možné explicitně určit, System.Text.Json nyní provádí nejbližší předkládkové rozlišení za běhu. Toto rozlišení určuje nejvhodnější supertyp, se kterým se má hodnota serializovat.

  • Nový typ JsonStringEnumConverter<TEnum>převaděče . JsonStringEnumConverter Stávající třída není v nativní AOT podporovaná. Typy výčtu můžete komentovat následujícím způsobem:

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

    Další informace naleznete v tématu Serializace výčtových polí jako řetězců.

  • Nová JsonConverter.Type vlastnost umožňuje vyhledat typ negenerické JsonConverter instance:

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

    Vlastnost je nullable, protože vrací null instance JsonConverterFactory a typeof(T) instance JsonConverter<T> .

Řetězení generátorů zdrojů

Třída JsonSerializerOptions obsahuje novou TypeInfoResolverChain vlastnost, která doplňuje existující TypeInfoResolver vlastnost. Tyto vlastnosti se používají v přizpůsobení kontraktů pro řetězení zdrojových generátorů. Přidání nové vlastnosti znamená, že nemusíte zadávat všechny zřetězený komponenty na jednom místě volání – dají se přidat za fakt. TypeInfoResolverChain umožňuje také introspekci řetězu nebo odebrání součástí. Další informace naleznete v tématu Kombinování zdrojových generátorů.

Kromě toho JsonSerializerOptions.AddContext<TContext>() je nyní zastaralé. Byla nahrazena vlastnostmi TypeInfoResolver a TypeInfoResolverChain vlastnostmi. Další informace najdete v tématu SYSLIB0049.

Hierarchie rozhraní

.NET 8 přidává podporu serializace vlastností z hierarchií rozhraní.

Následující kód ukazuje příklad, kde vlastnosti z okamžitě implementovaného rozhraní a jeho základního rozhraní jsou serializovány.

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

Zásady pojmenování

JsonNamingPolicy zahrnuje nové zásady pojmenování pro snake_case (s podtržítkem) a kebab-case (s převody názvů pomlček). Použijte tyto zásady podobně jako existující JsonNamingPolicy.CamelCase zásady:

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

Další informace najdete v tématu Použití předdefinovaných zásad pojmenování.

Vlastnosti jen pro čtení

Nyní můžete deserializovat na pole nebo vlastnosti jen pro čtení (to znamená ty, které nemají přístupové objekty set ).

Chcete-li se přihlásit k této podpoře globálně, nastavte novou možnost PreferredObjectCreationHandling, na JsonObjectCreationHandling.Populatehodnotu . Pokud se jedná o problém s kompatibilitou, můžete funkci povolit podrobněji tím, že atribut umístíte [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] na konkrétní typy, jejichž vlastnosti se mají naplnit, nebo na jednotlivé vlastnosti.

Představte si například následující kód, který deserializuje do CustomerInfo typu, který má dvě vlastnosti jen pro čtení.

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

Před .NET 8 byly vstupní hodnoty ignorovány a NamesCompany vlastnosti si zachovaly výchozí hodnoty.

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

Nyní se vstupní hodnoty používají k naplnění vlastností jen pro čtení během deserializace.

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

Další informace o naplnění deserializačního chování naleznete v tématu Naplnění inicializovaných vlastností.

Zakázání výchozího nastavení založeného na reflexi

Nyní můžete zakázat použití serializátoru založeného na reflexi ve výchozím nastavení. Toto zakázání je užitečné, pokud se chcete vyhnout náhodnému rootingu součástí reflexe, které se ani nepoužívají, zejména v oříznutých a nativních aplikacích AOT. Chcete-li zakázat výchozí serializace založené na reflexi tím, že vyžaduje, aby JsonSerializerOptions byl argument předán JsonSerializer serializaci a deserializační metody, nastavte JsonSerializerIsReflectionEnabledByDefault msBuild vlastnost v false souboru projektu.

Pomocí nového IsReflectionEnabledByDefault rozhraní API zkontrolujte hodnotu přepínače funkcí. Pokud vytváříte knihovnu, která je založená na System.Text.Jsonknihovně, můžete se spolehnout na to, že vlastnost nakonfiguruje výchozí hodnoty, aniž byste omylem zakládali komponenty reflexe.

Další informace najdete v tématu Zakázání výchozích hodnot reflexe.

Nové metody rozhraní JSONNode API

Mezi tyto JsonNode typy System.Text.Json.Nodes.JsonArray patří následující nové metody.

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

Neveřejní členové

Pro daný typ můžete vyjádřit výslovný souhlas s neveřejné členy do kontraktu serializace pomocí JsonIncludeAttribute anotací JsonConstructorAttribute atributů.

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

Další informace naleznete v tématu Použití neměnných typů a neveřejných členů a přístupových objektů.

Rozhraní API deserializace streamování

.NET 8 zahrnuje nové IAsyncEnumerable<T> metody rozšíření deserializace streamování, například GetFromJsonAsAsyncEnumerable. Podobné metody existovaly, které vracejí Task<TResult>například HttpClientJsonExtensions.GetFromJsonAsync. Nové metody rozšíření vyvolávají rozhraní API streamování a vrací IAsyncEnumerable<T>.

Následující kód ukazuje, jak můžete použít nové metody rozšíření.

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

Metoda rozšíření WithAddedModifier

Nová WithAddedModifier(IJsonTypeInfoResolver, Action<JsonTypeInfo>) metoda rozšíření umožňuje snadno zavést úpravy serializačních kontraktů libovolných IJsonTypeInfoResolver instancí.

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

Nové přetížení JsonContent.Create

Teď můžete vytvářet JsonContent instance pomocí trimově bezpečných nebo zdrojových kontraktů. Nové metody jsou:

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

Zablokování instance JsonSerializerOptions

Následující nové metody umožňují řídit, kdy JsonSerializerOptions je instance zablokovaná:

  • JsonSerializerOptions.MakeReadOnly()

    Toto přetížení je navržené tak, aby bylo bezpečné pro oříznutí, a proto vyvolá výjimku v případech, kdy instance možností nebyla nakonfigurována s překladačem.

  • JsonSerializerOptions.MakeReadOnly(Boolean)

    Pokud toto přetížení předáte true , naplní instanci možností výchozím překladačem reflexe, pokud chybí. Tato metoda je označena RequiresUnreferenceCode/RequiresDynamicCode a proto není vhodná pro nativní aplikace AOT.

Nová IsReadOnly vlastnost umožňuje zkontrolovat, jestli je instance možností zablokovaná.

Abstrakce času

Nová TimeProvider třída a ITimer rozhraní přidávají funkce abstrakce času, což umožňuje napodobení času v testovacích scénářích. Kromě toho můžete použít abstrakci času k napodobení Task operací, které spoléhají na průběh času pomocí Task.Delay a Task.WaitAsync. Abstrakce času podporuje následující základní časové operace:

  • Načtení místního času a času UTC
  • Získání časového razítka pro měření výkonu
  • Vytvoření časovače

Následující fragment kódu ukazuje některé příklady použití.

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

Vylepšení UTF8

Pokud chcete povolit psaní řetězcového vyjádření typu do cílového rozsahu, implementujte nové IUtf8SpanFormattable rozhraní vašeho typu. Toto nové rozhraní úzce souvisí ISpanFormattables , ale cílÍ UTF8 a Span<byte> místo UTF16 a Span<char>.

IUtf8SpanFormattable byla implementována ve všech primitivních typech (plus ostatní) se stejnou sdílenou logikou bez ohledu na to, zda cílení string, Span<char>nebo Span<byte>. Má plnou podporu pro všechny formáty (včetně nového binárního specifikátoru "B") a všech jazykových verzí. To znamená, že teď můžete formátovat přímo na formát UTF8 z Byte, , , Char, DateOnly, GuidDateTimeIPNetworkDoubleDecimalDateTimeOffsetHalfIPAddress, . SingleSByteNFloatRuneUInt32UInt16UInt64TimeSpanUInt128UIntPtrInt16Int64VersionInt32Int128IntPtrTimeOnlyComplex

Nové Utf8.TryWrite metody poskytují protějšk založený na UTF8 stávající MemoryExtensions.TryWrite metody, které jsou založené na UTF16. Syntaxi interpolovaného řetězce můžete použít k naformátování komplexního výrazu přímo do rozsahu bajtů UTF8, například:

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

Implementace rozpoznává IUtf8SpanFormattable hodnoty formátu a používá jejich implementace k zápisu reprezentací UTF8 přímo do cílového rozsahu.

Implementace také využívá novou Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32) metodu, která společně s jeho Encoding.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32) protějškem podporuje kódování a dekódování do cílového rozsahu. Pokud rozsah není dostatečně dlouhý pro uložení výsledného stavu, vrátí false se metody místo vyvolání výjimky.

Metody pro práci s náhodností

System.Security.Cryptography.RandomNumberGenerator Typy System.Random představují dvě nové metody pro práci s náhodností.

GetItems<T>()

Nové System.Random.GetItems a System.Security.Cryptography.RandomNumberGenerator.GetItems metody umožňují náhodně zvolit zadaný počet položek ze vstupní sady. Následující příklad ukazuje, jak použít System.Random.GetItems<T>() (v instanci poskytované Random.Shared vlastností) náhodně vložit 31 položek do pole. Tento příklad lze použít ve hře "Simon", kde hráči musí pamatovat posloupnost barevných tlačítek.

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

Nové Random.Shuffle a RandomNumberGenerator.Shuffle<T>(Span<T>) metody umožňují randomizovat pořadí rozsahu. Tyto metody jsou užitečné pro snížení předsudků trénování ve strojovém učení (takže první věc není vždy trénování a poslední věc vždy test).

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

Typy zaměřené na výkon

.NET 8 zavádí několik nových typů zaměřených na zlepšení výkonu aplikací.

  • Nový System.Collections.Frozen obor názvů zahrnuje typy kolekcí FrozenDictionary<TKey,TValue> a FrozenSet<T>. Tyto typy neumožňují po vytvoření kolekce žádné změny klíčů a hodnot. Tento požadavek umožňuje rychlejší operace čtení (například TryGetValue()). Tyto typy jsou zvláště užitečné pro kolekce, které se vyplní při prvním použití a po dobu trvání dlouhodobé služby, například:

    private static readonly FrozenDictionary<string, bool> s_configurationData =
        LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
    
    // ...
    if (s_configurationData.TryGetValue(key, out bool setting) && setting)
    {
        Process();
    }
    
  • Metody, jako je MemoryExtensions.IndexOfAny vyhledání prvního výskytu jakékoli hodnoty v předané kolekci. Nový System.Buffers.SearchValues<T> typ je navržen tak, aby byl předán těmto metodám. Rozhraní .NET 8 odpovídajícím způsobem přidává nová přetížení metod, jako MemoryExtensions.IndexOfAny je například akceptace instance nového typu. Když vytvoříte instanci SearchValues<T>, všechna data potřebná k optimalizaci následných hledání se odvozí v danou chvíli, což znamená, že práce se provádí předem.

  • Nový System.Text.CompositeFormat typ je užitečný pro optimalizaci formátových řetězců, které nejsou v době kompilace známé (například pokud je formátovací řetězec načten ze souboru prostředků). Trochu navíc se věnuje práci předem, například parsování řetězce, ale ušetří to práci před každým použitím.

    private static readonly CompositeFormat s_rangeMessage =
        CompositeFormat.Parse(LoadRangeMessageResource());
    
    // ...
    static string GetMessage(int min, int max) =>
        string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
    
  • Nové System.IO.Hashing.XxHash3 typy System.IO.Hashing.XxHash128 poskytují implementace rychlých hash algoritmů XXH3 a XXH128.

System.Numerics a System.Runtime.Intrinsics

Tato část popisuje vylepšení System.Numerics oborů názvů a System.Runtime.Intrinsics oborů názvů.

  • Vector256<T>Matrix4x4 a Matrix3x2mají vylepšenou hardwarovou akceleraci na platformě .NET 8. Například Vector256<T> byl znovu zkompimentován na interně 2x Vector128<T> operace, pokud je to možné. To umožňuje částečnou akceleraci některých funkcí, pokud Vector128.IsHardwareAccelerated == true ale Vector256.IsHardwareAccelerated == false, například v Arm64.
  • Hardwarové vnitřní objekty jsou nyní opatřeny poznámkami s atributem ConstExpected . Tím zajistíte, že uživatelé budou vědět, kdy základní hardware očekává konstantu, a proto když nekontinuálně hodnota může neočekávaně poškodit výkon.
  • Rozhraní Lerp(TSelf, TSelf, TSelf)Lerp API bylo přidáno do IFloatingPointIeee754<TSelf>float (Single), (Double) double a Half. Toto rozhraní API umožňuje efektivní a správné provedení lineární interpolace mezi dvěma hodnotami.

Vector512 a AVX-512

Rozšíření podpory SIMD pro .NET Core 3.0 tak, aby zahrnovalo rozhraní API hardwarových vnitřních objektů specifických pro platformu pro platformu x86/x64. Přidání podpory .NET 5 pro Arm64 a .NET 7 přidalo hardwarové vnitřní funkce pro různé platformy. .NET 8 dále podporuje SIMD zavedením Vector512<T> a podporou instrukcí Intel Advanced Vector Extensions 512 (AVX-512).

Konkrétně .NET 8 zahrnuje podporu následujících klíčových funkcí AVX-512:

  • 512bitové vektorové operace
  • Další 16 registrů SIMD
  • Další pokyny pro 128bitové, 256bitové a 512bitové vektory

Pokud máte hardware, který tuto funkci podporuje, teď Vector512.IsHardwareAccelerated hlásí true.

.NET 8 také přidá do oboru názvů několik tříd specifických pro platformu System.Runtime.Intrinsics.X86 :

Tyto třídy mají stejný obecný tvar jako ostatní architektury instrukční sady (ISA), ve kterých zpřístupňují IsSupported vlastnost a vnořenou Avx512F.X64 třídu pro pokyny dostupné pouze pro 64bitové procesy. Kromě toho má každá třída vnořenou Avx512F.VL třídu, která zveřejňuje Avx512VL rozšíření (délka vektoru) pro odpovídající instrukční sadu.

I když ve svém kódu explicitně nepoužíváte Vector512konkrétní nebo Avx512Fspecifické pokyny, pravděpodobně budete mít stále prospěch z nové podpory AVX-512. JIT může využít další registry a pokyny implicitně při použití Vector128<T> nebo Vector256<T>. Základní knihovna tříd používá tyto hardwarové vnitřní prvky interně ve většině operací vystavených Span<T>ReadOnlySpan<T> a v mnoha z matematických rozhraní API vystavených pro primitivní typy.

Ověření dat

Obor System.ComponentModel.DataAnnotations názvů zahrnuje nové atributy ověření dat určené pro scénáře ověřování v cloudových nativních službách. I když jsou existující DataAnnotations validátory zaměřené na typické ověřování zadávání dat uživatelského rozhraní, jako jsou pole ve formuláři, jsou nové atributy navržené tak, aby ověřovaly data bez uživatele, jako jsou možnosti konfigurace. Kromě nových atributů byly do typů RangeAttributeRequiredAttribute přidány nové vlastnosti.

Nové rozhraní API Popis
RangeAttribute.MinimumIsExclusive
RangeAttribute.MaximumIsExclusive
Určuje, zda jsou hranice zahrnuty do povoleného rozsahu.
System.ComponentModel.DataAnnotations.LengthAttribute Určuje dolní i horní mez pro řetězce nebo kolekce. Například [Length(10, 20)] vyžaduje alespoň 10 prvků a maximálně 20 prvků v kolekci.
System.ComponentModel.DataAnnotations.Base64StringAttribute Ověří, zda je řetězec platným vyjádřením Base64.
System.ComponentModel.DataAnnotations.AllowedValuesAttribute
System.ComponentModel.DataAnnotations.DeniedValuesAttribute
Zadejte seznamy povolených položek a v uvedeném pořadí odepřít seznamy. Například [AllowedValues("apple", "banana", "mango")].

Metriky

Nová rozhraní API umožňují připojit značky páru klíč-hodnota k Meter objektům a Instrument k nim při jejich vytváření. Agregátory publikovaných měření metrik můžou používat značky k odlišení agregovaných hodnot.

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

Mezi nová rozhraní API patří:

Kryptografie

.NET 8 přidává podporu primitivních hodnot hash SHA-3. (Sha-3 v současné době podporuje Linux s OpenSSL 1.1.1 nebo novějším a Windows 11 buildem 25324 nebo novějším.) Rozhraní API, kde je k dispozici SHA-2, teď nabízejí kompliment SHA-3. To zahrnuje SHA3_256, SHA3_384a SHA3_512 pro hashování; HMACSHA3_256, HMACSHA3_384, a HMACSHA3_512 pro HMAC; HashAlgorithmName.SHA3_256, HashAlgorithmName.SHA3_384a HashAlgorithmName.SHA3_512 pro hashování, kde je algoritmus konfigurovatelný; a RSAEncryptionPadding.OaepSHA3_384RSAEncryptionPadding.OaepSHA3_256, a pro RSAEncryptionPadding.OaepSHA3_512 šifrování RSA OAEP.

Následující příklad ukazuje, jak používat rozhraní API, včetně SHA3_256.IsSupported vlastnosti k určení, jestli platforma podporuje SHA-3.

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

Podpora SHA-3 je v současné době zaměřená na podporu kryptografických primitiv. Konstrukce a protokoly vyšší úrovně se neočekává, že zpočátku plně podporují SHA-3. Mezi tyto protokoly patří certifikáty SignedXmlX.509 a COSE.

Sítě

Podpora proxy serveru HTTPS

Doteď proxy typy, které HttpClient podporovaly všechny povolené "man-in-the-middle", aby viděly, ke které lokalitě se klient připojuje, a to i pro identifikátory URI HTTPS. HttpClient teď podporuje proxy protokol HTTPS, který vytvoří šifrovaný kanál mezi klientem a proxy serverem, aby se všechny požadavky mohly zpracovávat s plnou ochranou osobních údajů.

Chcete-li povolit proxy server HTTPS, nastavte proměnnou all_proxy prostředí nebo použijte WebProxy třídu k řízení proxy programově.

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

Třídu můžete také použít WebProxy k řízení proxy programově.

Metody ZipFile založené na streamu

.NET 8 obsahuje nová přetížení ZipFile.CreateFromDirectory , která vám umožní shromáždit všechny soubory zahrnuté v adresáři a zazipovat je a pak uložit výsledný soubor ZIP do poskytnutého datového proudu. Podobně nové ZipFile.ExtractToDirectory přetížení umožňují poskytnout datový proud obsahující komprimovaný soubor a extrahovat jeho obsah do systému souborů. Toto jsou nová přetížení:

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

Tato nová rozhraní API můžou být užitečná v případě omezení místa na disku, protože se vyhýbají použití disku jako přechodného kroku.

Knihovny rozšíření

Tato část obsahuje následující dílčí témata:

Služby DI s klíči

Služby injektáže závislostí s klíči poskytují prostředky pro registraci a načítání služeb DI pomocí klíčů. Pomocí klíčů můžete určit rozsah způsobu registrace a využívání služeb. Toto jsou některá nová rozhraní API:

Následující příklad ukazuje, jak používat služby DI s klíči.

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

Další informace najdete v tématu dotnet/runtime#64427.

Hostované služby životního cyklu

Hostované služby teď mají více možností pro spuštění během životního cyklu aplikace. IHostedService a StartAsyncStopAsyncnyní IHostedLifecycleService poskytuje tyto další metody:

Tyto metody se spouští před a za existujícími body.

Následující příklad ukazuje, jak používat nová rozhraní API.

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

Další informace najdete v tématu dotnet/runtime#86511.

Ověřování možností

Generátor zdrojů

Abychom snížili režii při spuštění a vylepšili sadu funkcí ověřování, zavedli jsme generátor zdrojového kódu, který implementuje logiku ověřování. Následující kód ukazuje ukázkové modely a třídy validátoru.

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

Pokud vaše aplikace používá injektáž závislostí, můžete ověření vložit, jak je znázorněno v následujícím ukázkovém kódu.

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

Typ ValidateOptionsResultBuilder

.NET 8 zavádí ValidateOptionsResultBuilder typ, který usnadňuje vytvoření objektu ValidateOptionsResult . Důležité je, že tento tvůrce umožňuje akumulace více chyb. Dříve bylo vytvoření objektu potřebného ValidateOptionsResult k implementaci IValidateOptions<TOptions>.Validate(String, TOptions) obtížné a někdy vedlo k chybám vrstveného ověřování. Pokud došlo k více chybám, proces ověřování se často zastavil při první chybě.

Následující fragment kódu ukazuje příklad použití 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 – konstruktory

LoggerMessageAttribute nyní nabízí další přetížení konstruktoru. Dříve jste museli zvolit konstruktor bez parametrů nebo konstruktor, který vyžadoval všechny parametry (ID události, úroveň protokolu a zprávu). Nové přetížení nabízejí větší flexibilitu při zadávání požadovaných parametrů s omezeným kódem. Pokud nezadáte ID události, systém ho automaticky vygeneruje.

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

Metriky rozšíření

IMeterFactory – rozhraní

Nové IMeterFactory rozhraní můžete zaregistrovat v kontejnerech injektáže závislostí (DI) a použít ho k vytvoření Meter objektů izolovaným způsobem.

IMeterFactory Zaregistrujte kontejner DI pomocí výchozí implementace továrny měřiče:

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

Příjemci pak mohou získat objekt pro vytváření měřičů a použít ho k vytvoření nového Meter objektu.

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

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

Meter meter = meterFactory.Create(options);

MetricCollector<T> – třída

Nová MetricCollector<T> třída umožňuje zaznamenávat měření metrik spolu s časovými razítky. Kromě toho třída nabízí flexibilitu použít poskytovatele času podle vašeho výběru pro přesné generování časového razítka.

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

Aktualizovaný balíček NuGet System.Numerics.Tensors obsahuje rozhraní API v novém TensorPrimitives oboru názvů, která přidávají podporu pro operace tensoru. Primitivní tensor optimalizují úlohy náročné na data, jako jsou úlohy umělé inteligence a strojového učení.

Úlohy umělé inteligence, jako je sémantické vyhledávání a načítání rozšířené generace (RAG), rozšiřují možnosti přirozeného jazyka velkých jazykových modelů, jako je ChatGPT, rozšířením výzev s relevantními daty. Pro tyto úlohy jsou zásadní operace s vektory, jako je kosinus podobnost , aby našli nejrelevavantnější data pro zodpovězení otázky. Balíček System.Numerics.Tensors.TensorPrimitives poskytuje rozhraní API pro vektorové operace, což znamená, že nemusíte používat externí závislost nebo psát vlastní implementaci.

Tento balíček nahrazuje balíček System.Numerics.Tensors.

Další informace najdete v blogovém příspěvku o oznámení .NET 8 RC 2.

Viz také