A .NET 8-futtatókörnyezet újdonságai

Ez a cikk a .NET 8 .NET-futtatókörnyezetének új funkcióit ismerteti.

Teljesítménynövelő fejlesztések

A .NET 8 a kódlétrehozás és az igény szerinti fordítás (JIT) fejlesztéseit tartalmazza:

  • Az Arm64 teljesítménybeli fejlesztései
  • SIMD-fejlesztések
  • Az AVX-512 ISA-bővítmények támogatása (lásd: Vector512 és AVX-512)
  • Natív felhőbeli fejlesztések
  • JIT átviteli sebesség fejlesztései
  • Hurkok és általános optimalizálások
  • Optimalizált hozzáférés a következővel megjelölt mezőkhöz: ThreadStaticAttribute
  • Egymást követő regiszter-lefoglalás. Az Arm64 két utasítást tartalmaz a táblázatvektorok keresésére, amelyek megkövetelik, hogy a rekordoperndusaikban lévő összes entitás szerepeljenek egymást követő regiszterekben.
  • A JIT/NativeAOT mostantól feloldhatja és automatikusan vektorizálhatja a SIMD-vel végzett memóriaműveleteket, például az összehasonlítást, a másolást és a nullázást, ha meg tudja határozni a méretüket a fordításkor.

Emellett a dinamikus profilvezérelt optimalizálás (PGO) továbbfejlesztve lett, és alapértelmezés szerint engedélyezve van. Az engedélyezéshez már nem kell futtatókörnyezeti konfigurációs beállítást használnia. A dinamikus PGO együttműködik a rétegzett fordítással, hogy tovább optimalizálja a kódot a 0. rétegben üzembe helyezett további rendszerezések alapján.

A dinamikus PGO átlagosan körülbelül 15%-kal növeli a teljesítményt. A ~4600 tesztből álló teljesítményteszt-csomagban 23% 20%-os vagy annál nagyobb teljesítménybeli javulást tapasztalt.

Codegen-struktúra-előléptetés

A .NET 8 tartalmaz egy új fizikai előléptetési optimalizálási kódot a codegenhez, amely általánosítja a JIT szerkezetváltozók előléptetésének képességét. Ez az optimalizálás (más néven az aggregátumok skaláris helyettesítése) a strukturálási változók mezőit olyan primitív változókra cseréli, amelyeket a JIT ezután pontosabban tud okulni és optimalizálni.

A JIT már támogatja ezt az optimalizálást, de számos nagy korlátozással, például:

  • Csak négy vagy kevesebb mezővel rendelkező szerkezetek esetében támogatott.
  • Csak akkor támogatott, ha minden mező primitív típus volt, vagy egy egyszerű szerkezet, amely egy primitív típust burkolt.

A fizikai előléptetés megszünteti ezeket a korlátozásokat, ami számos régóta fennálló JIT-problémát javít ki.

Szemétgyűjtés

A .NET 8 funkcióval menet közben módosíthatja a memóriakorlátot. Ez olyan felhőszolgáltatás-forgatókönyvekben hasznos, ahol a kereslet jön és megy. A költséghatékonyság érdekében a szolgáltatásoknak felfelé és lefelé kell skálázniuk az erőforrás-felhasználást, ahogy az igények ingadoznak. Ha egy szolgáltatás a kereslet csökkenését észleli, a memóriakorlát csökkentésével csökkentheti az erőforrás-felhasználást. Korábban ez meghiúsult, mert a szemétgyűjtő (GC) nem tudott a változásról, és több memóriát foglalhat le, mint az új korlát. Ezzel a módosítással meghívhatja az RefreshMemoryLimit() API-t, hogy frissítse a GC-t az új memóriakorláttal.

Néhány korlátozást figyelembe kell venni:

  • 32 bites platformokon (például Windows x86 és Linux ARM) a .NET nem tud új halom kemény korlátot megállapítani, ha még nincs ilyen.
  • Előfordulhat, hogy az API egy nem nulla állapotkódot ad vissza, amely azt jelzi, hogy a frissítés meghiúsult. Ez akkor fordulhat elő, ha a leskálázás túl agresszív, és nem hagy helyet a GC-nek a manőverezéshez. Ebben az esetben fontolja meg a hívásokat GC.Collect(2, GCCollectionMode.Aggressive) az aktuális memóriahasználat csökkentése érdekében, majd próbálkozzon újra.
  • Ha a memóriakorlátot a GC úgy véli, hogy a folyamat képes kezelni az indítás során, a RefreshMemoryLimit hívás sikeres lesz, de nem fog tudni több memóriát használni, mint amennyit a korlátnak tekint.

Az alábbi kódrészlet bemutatja, hogyan hívhatja meg az API-t.

GC.RefreshMemoryLimit();

A memóriakorláthoz kapcsolódó GC-konfigurációs beállítások némelyikét is frissítheti. Az alábbi kódrészlet a halom kemény korlátját 100 mebibyte-ra (MiB) állítja be:

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

Az API akkor tud dobni, InvalidOperationException ha a kemény korlát érvénytelen, például negatív halom kemény korlát százalékos értéke esetén, és ha a korlát túl alacsony. Ez akkor fordulhat elő, ha a frissítés által beállított halom kemény korlátja az új AppData-beállítások vagy a tároló memóriakorlátjának változásai miatt alacsonyabb, mint a már véglegesített érték.

Globalizáció mobilalkalmazásokhoz

Az iOS, tvOS és MacCatalyst rendszerű mobilalkalmazások új hibrid globalizációs módot választhatnak, amely egy könnyebb ICU-csomagot használ. Hibrid módban a globalizációs adatok részben az ICU-csomagból, részben pedig a natív API-kba irányuló hívásokból származnak. A hibrid mód a mobileszközök által támogatott összes területi beállításhoz használható.

A hibrid mód leginkább olyan alkalmazásokhoz alkalmas, amelyek nem tudnak invariáns globalizációs módban működni, és olyan kultúrákat használnak, amelyeket mobilon vágtak le az ICU-adatokból. Akkor is használhatja, ha kisebb ICU-adatfájlt szeretne betölteni. (A icudt_hybrid.dat fájl 34,5%-kal kisebb, mint az alapértelmezett ICU-adatfájl icudt.dat.)

A hibrid globalizációs mód használatához állítsa az HybridGlobalization MSBuild tulajdonságot igaz értékre:

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

Néhány korlátozást figyelembe kell venni:

  • A natív API korlátozásai miatt nem minden globalizációs API támogatott hibrid módban.
  • A támogatott API-k némelyikének eltérő a viselkedése.

Ha ellenőrizni szeretné, hogy az alkalmazás érintett-e, tekintse meg a viselkedésbeli különbségeket.

Forrás által létrehozott COM-interop

A .NET 8 tartalmaz egy új forrásgenerátort, amely támogatja a COM-adapterekkel való üzemeltetést. A forrásgenerátor com-felületként való megjelöléséhez használhatja GeneratedComInterfaceAttribute a felületet. A forrásgenerátor ezután létrehoz egy kódot, amely lehetővé teszi a C#-kódból a nem felügyelt kódra való hívást. Emellett létrehoz egy kódot, amely lehetővé teszi a nem felügyelt kódból a C#-ba való hívást. Ez a forrásgenerátor integrálva van a GeneratedComInterfaceAttribute következővelLibraryImportAttribute, és a típusokat paraméterként használhatja, a -attributed metódusokban LibraryImportpedig visszaadhat típusokat.

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

A forrásgenerátor az új GeneratedComClassAttribute attribútumot is támogatja, így olyan típusokat adhat át, amelyek interfészeket implementálnak az GeneratedComInterfaceAttribute attribútummal nem felügyelt kódnak. A forrásgenerátor létrehozza az interfészeket megvalósító COM-objektum felfedéséhez szükséges kódot, és átirányítja a hívásokat a felügyelt implementációba.

Az attribútummal rendelkező GeneratedComInterfaceAttribute felületeken lévő metódusok ugyanazokat a típusokat támogatjákGeneratedComInterface, mint LibraryImportAttributeaz - LibraryImportAttribute attribútumos és GeneratedComClass-attribútumos típusok.

Ha a C#-kód csak -attribútumú felületet használ GeneratedComInterfaceegy COM-objektum nem felügyelt kódból való körbefuttatásához, vagy egy felügyelt objektum C#-ból való körbefuttatásához a nem felügyelt kódnak való felfedéséhez, a tulajdonság beállításaival Options testre szabhatja, hogy melyik kódot hozza létre. Ezek a lehetőségek azt jelentik, hogy nem kell rendezőket írnia olyan forgatókönyvekhez, amelyekről tudja, hogy nem lesznek használatban.

A forrásgenerátor az új StrategyBasedComWrappers típussal hozza létre és kezeli a COM-objektumburkolókat és a felügyelt objektumburkolókat. Ez az új típus kezeli a com-interop várt .NET felhasználói élményét, miközben testreszabási pontokat biztosít a haladó felhasználók számára. Ha az alkalmazás saját mechanizmussal rendelkezik a COM-ból származó típusok meghatározásához, vagy ha olyan forgatókönyveket kell támogatnia, amelyeket a forrás által létrehozott COM jelenleg nem támogat, fontolja meg az új StrategyBasedComWrappers típus használatát a forgatókönyv hiányzó funkcióinak hozzáadásához, és ugyanazt a .NET felhasználói élményt kapja a COM-típusok esetében.

Ha Visual Studio-t használ, az új elemzők és kódjavítások megkönnyítik a meglévő COM-interop-kód konvertálását a forrás által létrehozott interop használatára. Minden olyan felület mellett, amely rendelkezik, ComImportAttributeegy villanykörte lehetőséget kínál a forrás által generált interopra való konvertálásra. A javítás módosítja a felületet az GeneratedComInterfaceAttribute attribútum használatára. És minden olyan osztály mellett, amely egy interfészt GeneratedComInterfaceAttributevalósít meg, egy villanykörte lehetőséget kínál az GeneratedComClassAttribute attribútum típushoz való hozzáadására. A típusok konvertálása után áthelyezheti a DllImport metódusokat a használatba LibraryImportAttribute.

Korlátozások

A COM-forrásgenerátor nem támogatja a lakás affinitást, a new kulcsszót használva aktiválja a COM CoClass-ot, és a következő API-kat:

Konfigurációkötési forrásgenerátor

A .NET 8 egy forrásgenerátort vezet be, amely AOT-t és vágásbarát konfigurációt biztosít ASP.NET Core-ban. A generátor a már meglévő tükröződésalapú megvalósítás alternatíva.

A forrásgenerátor mintavételezi a Configure(TOptions), Bindés Get a hívásokat a típusadatok lekéréséhez. Ha a generátor engedélyezve van egy projektben, a fordító implicit módon választja ki a generált metódusokat a már meglévő tükröződésalapú keretrendszer-implementációk helyett.

A generátor használatához nincs szükség forráskódmódosításra. Alapértelmezés szerint engedélyezve van az AOT-webalkalmazásokban. Más projekttípusok esetén a forrásgenerátor alapértelmezés szerint ki van kapcsolva, de a EnableConfigurationBindingGenerator tulajdonságot true a projektfájlban beállíthatja:

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

Az alábbi kód egy példa a kötőanyag meghívására.

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

Alapvető .NET-kódtárak

Ez a szakasz a következő altopikát tartalmazza:

Önkifejezés ion

A függvénymutatók a .NET 5-ben lettek bevezetve, de a tükrözés megfelelő támogatása akkor még nem lett hozzáadva. Ha függvénymutatót használ typeof vagy tükröz, typeof(delegate*<void>()) például egy függvénymutatót, vagy FieldInfo.FieldType egy függvényt IntPtr ad vissza. A .NET 8-tól kezdve a rendszer egy objektumot System.Type ad vissza. Ez a típus hozzáférést biztosít a függvénymutató metaadataihoz, beleértve a hívási konvenciók, a visszatérési típus és a paraméterek elérését.

Feljegyzés

A függvénymutató-példány, amely egy függvény fizikai címe, továbbra is egy .IntPtr Csak a tükröződés típusa változott meg.

Az új funkció jelenleg csak a CoreCLR-futtatókörnyezetben és MetadataLoadContexta .

Új API-k lettek hozzáadva System.Typeaz olyanhoz, mint IsFunctionPointerpéldául az , és a System.Reflection.PropertyInfo, System.Reflection.FieldInfoés System.Reflection.ParameterInfo. Az alábbi kód bemutatja, hogyan használhatja az új API-kat a tükröződéshez.

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

Az előző példa a következő kimenetet hozza létre:

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

Szerializációs

A .NET 8 szerializálási és deszerializálási funkciójában számos fejlesztés történt System.Text.Json . Testre szabhatja például azoknak a tagoknak a kezelését, akik nem szerepelnek a JSON hasznos adataiban.

A következő szakaszok a szerializálás egyéb fejlesztéseit ismertetik:

A JSON-szerializálással kapcsolatos további információkért lásd a JSON szerializálását és deszerializálását a .NET-ben.

További típusok beépített támogatása

A szerializáló beépített támogatást nyújt a következő további típusokhoz.

  • Half, Int128és UInt128 numerikus típusokat.

    Console.WriteLine(JsonSerializer.Serialize(
        [ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ]
    ));
    // [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]
    
  • Memory<T>értékeket.ReadOnlyMemory<T> byte az értékek Base64-sztringekre, más típusok pedig JSON-tömbökre vannak szerializálva.

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

Forrásgenerátor

A .NET 8 tartalmazza a System.Text.Json forrásgenerátor fejlesztéseit, amelyek célja, hogy a natív AOT-élményt a tükröződésalapú szerializálóval egyenértékben alakítsa ki. Példa:

  • A forrásgenerátor mostantól támogatja a szerializálási típusokat required és init tulajdonságokat. Ezeket már támogatták a tükröződésalapú szerializálásban.

  • A forrás által létrehozott kód továbbfejlesztett formázása.

  • JsonSourceGenerationOptionsAttribute funkció paritása a JsonSerializerOptions. További információt a Beállítások megadása (forrásgenerálás) című témakörben talál.

  • További diagnosztikák (például SYSLIB1034 és SYSLIB1039).

  • Ne tartalmazzon figyelmen kívül hagyott vagy elérhetetlen tulajdonságokat.

  • Tetszőleges típusú deklarációk beágyazásának JsonSerializerContext támogatása.

  • A fordító által létrehozott vagy kimondhatatlan típusok támogatása gyengén beírt forrásgenerálási forgatókönyvekben. Mivel a fordító által létrehozott típusok nem határozhatók meg explicit módon a forrásgenerátor által, System.Text.Json most futtatáskor a legközelebbi ősfelbontást hajtja végre. Ez a felbontás határozza meg a legmegfelelőbb szupertípust, amellyel szerializálhatja az értéket.

  • Új konvertertípus JsonStringEnumConverter<TEnum>. A natív AOT nem támogatja a meglévő JsonStringEnumConverter osztályt. Az enumerálási típusokat az alábbiak szerint jegyzetelheti:

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

    További információt az enumerálási mezők sztringként való szerializálása című témakörben talál.

  • Az új JsonConverter.Type tulajdonság lehetővé teszi egy nem általános JsonConverter példány típusának keresését:

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

    A tulajdonság null értékű, mivel a példányokat nullJsonConverterFactory és typeof(T) a JsonConverter<T> példányokat adja vissza.

Láncforrás-generátorok

Az JsonSerializerOptions osztály egy új TypeInfoResolverChain tulajdonságot tartalmaz, amely kiegészíti a meglévő TypeInfoResolver tulajdonságot. Ezeket a tulajdonságokat a rendszer a forrásgenerátorok láncolásához használja a szerződések testreszabásához. Az új tulajdonság hozzáadása azt jelenti, hogy nem kell minden láncolt összetevőt megadnia egy hívási helyen– ezek a tény után is hozzáadhatók. TypeInfoResolverChain lehetővé teszi a lánc betekintőjét, vagy eltávolíthatja belőle az összetevőket. További információ: Forrásgenerátorok egyesítése.

Ráadásul JsonSerializerOptions.AddContext<TContext>() mára elavult. A tulajdonságok felülírták TypeInfoResolverTypeInfoResolverChain . További információ: SYSLIB0049.

Felületi hierarchiák

A .NET 8 támogatja a tulajdonságok felületi hierarchiákból való szerializálását.

Az alábbi kód egy példát mutat be, amelyben az azonnal implementált felület és az alapfelület tulajdonságai szerializálva vannak.

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

Elnevezési szabályzatok

JsonNamingPolicy Új elnevezési szabályzatokat snake_case tartalmaz (aláhúzásjellel) és kebab-case (kötőjeles) tulajdonságnév-átalakításokhoz. Használja ezeket a szabályzatokat a meglévő JsonNamingPolicy.CamelCase házirendhez hasonlóan:

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

További információ: Beépített elnevezési szabályzat használata.

Csak olvasható tulajdonságok

Most már deszerializálhat írásvédett mezőkre vagy tulajdonságokra (vagyis olyanokra, amelyek nem rendelkeznek tartozékokkal set ).

Ha globálisan szeretne csatlakozni ehhez a támogatáshoz, állítson be egy új lehetőséget a PreferredObjectCreationHandlingkövetkezőre JsonObjectCreationHandling.Populate: . Ha a kompatibilitás aggodalomra ad okot, részletesebben is engedélyezheti a funkciót, ha az [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] attribútumot olyan adott típusokra helyezi, amelyeknek a tulajdonságait ki kell tölteni, vagy az egyes tulajdonságokra.

Vegyük például az alábbi kódot, amely deszerializál egy CustomerInfo olyan típust, amely két írásvédett tulajdonsággal rendelkezik.

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

A .NET 8 előtt a bemeneti értékek figyelmen kívül lettek hagyva, és a NamesCompany tulajdonságok megtartották az alapértelmezett értékeket.

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

A bemeneti értékek most a deszerializálás során az írásvédett tulajdonságok feltöltésére szolgálnak.

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

A kitöltött deszerializálási viselkedésről további információt az inicializált tulajdonságok feltöltése című témakörben talál.

Tükröződésalapú alapértelmezett beállítás letiltása

Mostantól alapértelmezés szerint letilthatja a tükröződésalapú szerializálót. Ez a letiltás akkor hasznos, ha elkerüli a még használatban nem lévő tükröződési összetevők véletlen gyökerezését, különösen a levágott és natív AOT-alkalmazásokban. Ha úgy szeretné letiltani az alapértelmezett tükröződésalapú szerializálást, hogy JsonSerializerOptions argumentumot kell átadni a JsonSerializer szerializálási és deszerializálási módszereknek, állítsa be az JsonSerializerIsReflectionEnabledByDefault MSBuild tulajdonságot false a projektfájlba.

Az új IsReflectionEnabledByDefault API használatával ellenőrizze a funkciókapcsoló értékét. Ha Ön egy olyan kódtár-szerző, aki a tetején System.Text.Jsonépít, a tulajdonságra támaszkodva konfigurálhatja az alapértelmezett értékeket anélkül, hogy véletlenül a tükröződés összetevőit gyökerezteti.

További információ: A tükröződés alapértelmezéseinek letiltása.

Új JsonNode API-metódusok

System.Text.Json.Nodes.JsonArray A JsonNode típusok a következő új metódusokat tartalmazzák.

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

Nem nyilvános tagok

A nem nyilvános tagokat egy adott típus szerializálási szerződésébe választhatja széljegyzetek és JsonConstructorAttribute attribútumok használatávalJsonIncludeAttribute.

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

További információ: Nem módosítható típusok és nem nyilvános tagok és tartozékok használata.

Streamelt deszerializálási API-k

A .NET 8 új streamelt deszerializálási bővítménymetszeteket tartalmaz IAsyncEnumerable<T> , például GetFromJsonAsAsyncEnumerable. Hasonló metódusok léteznek, amelyek például HttpClientJsonExtensions.GetFromJsonAsyncvisszaadják a következőtTask<TResult>: . Az új bővítménymetelyek meghívják a streamelési API-kat, és visszaadják azokat IAsyncEnumerable<T>.

Az alábbi kód bemutatja, hogyan használhatja az új bővítménymetelyeket.

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

WithAddedModifier bővítménymetódus

Az új WithAddedModifier(IJsonTypeInfoResolver, Action<JsonTypeInfo>) bővítménymetódus lehetővé teszi az tetszőleges IJsonTypeInfoResolver példányok szerializálási szerződéseinek módosítását.

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

Új JsonContent.Create túlterhelések

Most már JsonContent létrehozhat példányokat a csökkentett vagy a forrás által létrehozott szerződések használatával. Az új módszerek a következők:

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

JsonSerializerOptions-példány rögzítése

A következő új metódusok segítségével szabályozhatja, JsonSerializerOptions hogy egy példány le van-e fagyasztva:

  • JsonSerializerOptions.MakeReadOnly()

    Ez a túlterhelés úgy van kialakítva, hogy biztonságos legyen, ezért kivételt jelent azokban az esetekben, amikor a beállításpéldány nincs feloldóval konfigurálva.

  • JsonSerializerOptions.MakeReadOnly(Boolean)

    Ha ezt a túlterhelést adja át true , a rendszer feltölti a beállításpéldányt az alapértelmezett tükröződési feloldóval, ha hiányzik. Ez a módszer meg van jelölve RequiresUnreferenceCode/RequiresDynamicCode , ezért nem megfelelő natív AOT-alkalmazásokhoz.

Az új IsReadOnly tulajdonság segítségével ellenőrizheti, hogy a beállításpéldány le van-e fagyasztva.

Idő absztrakciója

Az új TimeProvider osztály és ITimer felület idő absztrakciós funkciót ad hozzá, amely lehetővé teszi az idő szimulálását tesztforgatókönyvekben. Emellett az idő absztrakcióval szimulálhatja azokat Task a műveleteket, amelyek az idő előrehaladására Task.Delay támaszkodnak az és Task.WaitAsynca . Az idő absztrakciója a következő alapvető időműveleteket támogatja:

  • Helyi és UTC idő lekérése
  • Időbélyeg beszerzése a teljesítmény méréséhez
  • Időzítő létrehozása

Az alábbi kódrészlet néhány használati példát mutat be.

// 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-fejlesztések

Ha engedélyezni szeretné a típus sztringszerű ábrázolását egy céltartományba, implementálja az új IUtf8SpanFormattable felületet a típuson. Ez az új felület szorosan kapcsolódik az UTF8-hoz ISpanFormattable, és Span<byte> az UTF16 és Span<char>a .

IUtf8SpanFormattable az összes primitív típuson (plusz másokon) implementálva lett, pontosan ugyanazzal a megosztott logikával, akár célként string, Span<char>akár Span<byte>. Teljes mértékben támogatja az összes formátumot (beleértve az új "B" bináris azonosítót) és az összes kultúrát. Ez azt jelenti, hogy mostantól közvetlenül az UTF8 formátumba formázhatja a következőt: Byte, Complex, Char, HalfUInt32GuidDoubleDecimalIPAddressDateTimeOffsetDateTimeDateOnlyIPNetworkInt16SByteNFloatSingleIntPtrRuneInt128TimeOnlyTimeSpanInt64Int32UInt64UInt128UInt16UIntPtrés .Version

Az új Utf8.TryWrite metódusok UTF8-alapú megfelelőt biztosítanak a meglévő MemoryExtensions.TryWrite metódusok számára, amelyek UTF16-alapúak. Az interpolált sztringszintaxissal egy összetett kifejezést közvetlenül UTF8 bájtok közé formázhat, például:

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

Az implementáció felismeri a formátumértékeket IUtf8SpanFormattable , és implementációikkal közvetlenül a céltartományba írja az UTF8-ábrázolásokat.

Az implementáció az új Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32) módszert is használja, amely a megfelelőjével Encoding.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32) együtt támogatja a kódolást és a dekódolást céltartományba. Ha a span nem elég hosszú az eredményül kapott állapot tárolásához, a metódusok a kivétel kivetése helyett térnek vissza false .

A véletlenszerűség használatának módszerei

A System.Random típusok System.Security.Cryptography.RandomNumberGenerator két új módszert vezetnek be a véletlenszerűség használatához.

GetItems<T>()

Az új System.Random.GetItems és System.Security.Cryptography.RandomNumberGenerator.GetItems a metódusok lehetővé teszik, hogy véletlenszerűen válasszon ki egy megadott számú elemet egy bemeneti készletből. Az alábbi példa bemutatja, hogyan használható System.Random.GetItems<T>() (a Random.Shared tulajdonság által biztosított példányon) 31 elem véletlenszerű beszúrására egy tömbbe. Ez a példa használható a "Simon" játékban, ahol a játékosoknak színes gombok sorozatára kell emlékeznie.

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

T>() shuffle<

Az új Random.Shuffle és RandomNumberGenerator.Shuffle<T>(Span<T>) metódusokkal véletlenszerűen rendezheti a span sorrendjét. Ezek a módszerek hasznosak a gépi tanulás betanítási torzításainak csökkentéséhez (tehát az első dolog nem mindig a betanítás, az utolsó dolog pedig mindig a tesztelés).

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

Teljesítményközpontú típusok

A .NET 8 számos új típust vezet be, amelyek célja az alkalmazások teljesítményének javítása.

  • Az új System.Collections.Frozen névtér tartalmazza a gyűjteménytípusokat FrozenDictionary<TKey,TValue> és FrozenSet<T>a . Ezek a típusok nem teszik lehetővé a kulcsok és értékek módosítását a gyűjtemény létrehozása után. Ez a követelmény gyorsabb olvasási műveleteket tesz lehetővé (például TryGetValue()). Ezek a típusok különösen hasznosak olyan gyűjtemények esetében, amelyek első használatkor vannak feltöltve, majd tartósan megmaradnak egy hosszú élettartamú szolgáltatás időtartama alatt, például:

    private static readonly FrozenDictionary<string, bool> s_configurationData =
        LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
    
    // ...
    if (s_configurationData.TryGetValue(key, out bool setting) && setting)
    {
        Process();
    }
    
  • Az olyan metódusok, mint MemoryExtensions.IndexOfAny például az átadott gyűjteményben lévő értékek első előfordulásának keresése. Az új System.Buffers.SearchValues<T> típust úgy tervezték, hogy ezeket a módszereket át lehessen adni. Ennek megfelelően a .NET 8 új túlterheléseket ad hozzá az olyan metódusokhoz, mint amelyek MemoryExtensions.IndexOfAny elfogadják az új típusú példányt. Amikor létrehoz egy példányt SearchValues<T>, a későbbi keresések optimalizálásához szükséges összes adat le lesz származtatva , ami azt jelenti, hogy a munka előre el van végezve.

  • Az új System.Text.CompositeFormat típus a fordításkor nem ismert formázási sztringek optimalizálásához hasznos (például ha a formátumsztring egy erőforrásfájlból van betöltve). Egy kis extra időt töltenek előre, hogy olyan munkát végezzenek, mint a sztring elemzése, de ez menti a munkát attól, hogy minden egyes használaton elvégezve legyen.

    private static readonly CompositeFormat s_rangeMessage =
        CompositeFormat.Parse(LoadRangeMessageResource());
    
    // ...
    static string GetMessage(int min, int max) =>
        string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
    
  • Az új System.IO.Hashing.XxHash3 és System.IO.Hashing.XxHash128 típusok a gyors XXH3- és XXH128-kivonatoló algoritmusok implementációit biztosítják.

System.Numerics és System.Runtime.Intrinsics

Ez a szakasz a névterek és System.Runtime.Intrinsics a System.Numerics névterek fejlesztéseit ismerteti.

  • Vector256<T>, Matrix3x2és Matrix4x4 jobb hardvergyorsítást érhet el a .NET 8-on. A rendszer például Vector256<T> a belső 2x Vector128<T> műveletekre lett átvenve, ahol lehetséges. Ez lehetővé teszi bizonyos függvények részleges gyorsítását, ha Vector128.IsHardwareAccelerated == true azonban Vector256.IsHardwareAccelerated == false, például arm64-en.
  • A hardver belső jellemzői mostantól az attribútummal ConstExpected vannak eljegyzve. Ez biztosítja, hogy a felhasználók tisztában legyenek azzal, hogy a mögöttes hardver mikor számít állandóra, és ezért amikor egy nem állandó érték váratlanul ronthatja a teljesítményt.
  • Az Lerp(TSelf, TSelf, TSelf)Lerp API hozzá lett adva, és így hozzá float lett adva IFloatingPointIeee754<TSelf> a (Single), double (Double) és Half. Ez az API lehetővé teszi a két érték közötti lineáris interpoláció hatékony és helyes végrehajtását.

Vector512 és AVX-512

A .NET Core 3.0 kiterjesztett SIMD-támogatása tartalmazza az x86/x64-hez készült platformspecifikus hardveres belső API-kat. A .NET 5 támogatja az Arm64-et és a .NET 7-et, és hozzáadta a platformfüggetlen hardveres belső elemet. A .NET 8 támogatja a SIMD-t az Intel Advanced Vector Extensions 512 (AVX-512) utasításainak bevezetésével Vector512<T> és támogatásával.

A .NET 8 az AVX-512 következő fő funkcióit támogatja:

  • 512 bites vektorműveletek
  • További 16 SIMD-regisztráció
  • További utasítások a 128 bites, 256 bites és 512 bites vektorokhoz

Ha olyan hardvere van, amely támogatja a funkciót, akkor Vector512.IsHardwareAccelerated most jelentést készít true.

A .NET 8 emellett több platformspecifikus osztályt is hozzáad a System.Runtime.Intrinsics.X86 névtérhez:

Ezek az osztályok ugyanazt az általános alakzatot követik, mint más utasításkészlet-architektúrák (ISA-k), mivel egy IsSupported tulajdonságot és egy beágyazott osztályt Avx512F.X64 tesznek elérhetővé, amely csak a 64 bites folyamatok számára érhető el. Emellett minden osztálynak van egy beágyazott Avx512F.VL osztálya, amely elérhetővé teszi a Avx512VL megfelelő utasításkészlet (vektorhossz) bővítményeit.

Még ha nem is használ Vector512kifejezetten -specific vagy Avx512F-specific utasításokat a kódban, valószínűleg továbbra is élvezheti az új AVX-512-támogatást. A JIT implicit módon kihasználhatja a további regiszterek és utasítások előnyeit a használat vagy Vector128<T>Vector256<T>a . Az alaposztály-kódtár ezeket a hardveres belső belső feltételeket használja a legtöbb, a primitív típusok számára közzétett matematikai API-k által Span<T> és ReadOnlySpan<T> számos műveletben.

Adatellenőrzés engedélyezése

A System.ComponentModel.DataAnnotations névtér új adatérvényesítési attribútumokat tartalmaz, amelyek a natív felhőbeli szolgáltatások érvényesítési forgatókönyveihez szolgálnak. Bár a már meglévő DataAnnotations érvényesítők a felhasználói felület adatbevitelének tipikus érvényesítésére irányulnak, például egy űrlap mezőire, az új attribútumok a nem felhasználó által megadott adatok, például a konfigurációs beállítások ellenőrzésére szolgálnak. Az új attribútumok mellett új tulajdonságokat is hozzáadtunk a RangeAttribute típusokhoz.RequiredAttribute

Új API Leírás
RangeAttribute.MinimumIsExclusive
RangeAttribute.MaximumIsExclusive
Megadja, hogy a korlátok bele legyenek-e foglalva az engedélyezett tartományba.
System.ComponentModel.DataAnnotations.LengthAttribute A sztringek vagy gyűjtemények alsó és felső határát is megadja. Ehhez például [Length(10, 20)] legalább 10 elemre és legfeljebb 20 elemre van szükség egy gyűjteményben.
System.ComponentModel.DataAnnotations.Base64StringAttribute Ellenőrzi, hogy egy sztring érvényes Base64-ábrázolás-e.
System.ComponentModel.DataAnnotations.AllowedValuesAttribute
System.ComponentModel.DataAnnotations.DeniedValuesAttribute
Adja meg az engedélyezési listákat és a tiltólistákat. Például: [AllowedValues("apple", "banana", "mango")].

Mérőszámok

Az új API-k segítségével kulcs-érték pár címkéket Meter és Instrument objektumokat csatolhat a létrehozásukkor. A közzétett metrikamérések összesítői a címkék használatával különböztethetik meg az összesített értékeket.

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

Az új API-k a következők:

Kriptográfia

A .NET 8 támogatja az SHA-3 kivonatolási primitíveket. (Az SHA-3-at jelenleg a Linux támogatja az OpenSSL 1.1.1 vagy újabb verziójával, valamint a Windows 11 25324-re vagy újabb buildre.) Az OLYAN API-k, ahol az SHA-2 elérhető, mostantól sha-3 bókot kínálnak. Ide tartozik SHA3_256a kivonatolás SHA3_512SHA3_384, HMACSHA3_256HMACSHA3_384a kivonatolás, a HMACSHA3_512 HMAC; HashAlgorithmName.SHA3_384HashAlgorithmName.SHA3_256HashAlgorithmName.SHA3_512 és a kivonatolás, ahol az algoritmus konfigurálható, valamint RSAEncryptionPadding.OaepSHA3_256az RSAEncryptionPadding.OaepSHA3_384RSAEncryptionPadding.OaepSHA3_512 RSA OAEP-titkosításhoz.

Az alábbi példa bemutatja, hogyan használhatja az API-kat, beleértve a SHA3_256.IsSupported tulajdonságot annak megállapításához, hogy a platform támogatja-e az SHA-3-at.

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

Az SHA-3 támogatása jelenleg a titkosítási primitívek támogatására irányul. A magasabb szintű szerkezetek és protokollok kezdetben nem támogatják teljes mértékben az SHA-3-at. Ezek a protokollok közé tartoznak az X.509-tanúsítványok és SignedXmla CO Standard kiadás.

Hálózatkezelés

HTTPS-proxy támogatása

Eddig az összeset támogató proxytípusok lehetővé tették, hogy HttpClient az ügyfél a HTTPS URI-k esetében is láthassa, hogy melyik helyhez csatlakozik az ügyfél. HttpClient Mostantól támogatja a HTTPS-proxyt, amely titkosított csatornát hoz létre az ügyfél és a proxy között, így az összes kérés teljes adatvédelemmel kezelhető.

A HTTPS-proxy engedélyezéséhez állítsa be a all_proxy környezeti változót, vagy használja az WebProxy osztályt a proxy programozott vezérléséhez.

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

Az osztály használatával WebProxy programozott módon is vezérelheti a proxyt.

Streamalapú ZipFile-metódusok

A .NET 8 új túlterheléseket ZipFile.CreateFromDirectory tartalmaz, amelyek lehetővé teszik, hogy összegyűjtse a könyvtárban lévő összes fájlt, és tömörítse őket, majd tárolja az eredményül kapott zip-fájlt a megadott streamben. Hasonlóképpen, az új ZipFile.ExtractToDirectory túlterhelések lehetővé teszik egy tömörített fájlt tartalmazó streamet, és kinyerheti annak tartalmát a fájlrendszerbe. Ezek az új túlterhelések:

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

Ezek az új API-k akkor lehetnek hasznosak, ha a lemezterület korlátozott, mivel nem kell köztes lépésként használniuk a lemezt.

Bővítménytárak

Ez a szakasz a következő altopikát tartalmazza:

Kulcsos DI-szolgáltatások

A kulcsos függőséginjektálási (DI) szolgáltatások lehetővé teszik a DI-szolgáltatások kulcsokkal történő regisztrálását és lekérését. A kulcsok használatával hatókört adhat a szolgáltatások regisztrálásának és felhasználásának. Ezek az új API-k:

Az alábbi példa bemutatja, hogyan használhatja a kulcsos DI-szolgáltatásokat.

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

További információ: dotnet/runtime#64427.

Üzemeltetett életciklus-szolgáltatások

Az üzemeltetett szolgáltatások mostantól több végrehajtási lehetőséggel rendelkeznek az alkalmazás életciklusa során. IHostedServiceStopAsyncés StartAsync most IHostedLifecycleService a következő további módszereket biztosítja:

Ezek a metódusok a meglévő pontok előtt és után futnak.

Az alábbi példa az új API-k használatát mutatja be.

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

További információ: dotnet/runtime#86511.

Beállítások érvényesítése

Forrásgenerátor

Az indítási többletterhelés csökkentése és az érvényesítési funkciókészlet javítása érdekében bevezettünk egy forráskódgenerátort, amely implementálja az érvényesítési logikát. Az alábbi kód példamodelleket és érvényesítő osztályokat mutat be.

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

Ha az alkalmazás függőséginjektálást használ, az alábbi példakódban látható módon injektálhatja az ellenőrzést.

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

ValidateOptionsResultBuilder típus

A .NET 8 bevezeti az ValidateOptionsResultBuilder objektum létrehozásának megkönnyítésére szolgáló típust ValidateOptionsResult . Fontos, hogy ez a szerkesztő lehetővé teszi több hiba felhalmozódását. Korábban a ValidateOptionsResult megvalósításhoz IValidateOptions<TOptions>.Validate(String, TOptions) szükséges objektum létrehozása nehéz volt, és néha rétegzett érvényesítési hibákat eredményezett. Ha több hiba történt, az ellenőrzési folyamat gyakran leállt az első hibánál.

Az alábbi kódrészlet a példahasználatot ValidateOptionsResultBuildermutatja be.

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 konstruktorok

LoggerMessageAttribute mostantól további konstruktor-túlterheléseket is kínál. Korábban a paraméter nélküli konstruktort vagy az összes paramétert (eseményazonosítót, naplószintet és üzenetet) igénylő konstruktort kellett választania. Az új túlterhelések nagyobb rugalmasságot biztosítanak a szükséges paraméterek csökkentett kóddal való megadásában. Ha nem ad meg eseményazonosítót, a rendszer automatikusan létrehoz egyet.

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

Bővítmények metrikái

IMeterFactory felület

Regisztrálhatja az új IMeterFactory felületet a függőséginjektálási (DI) tárolókban, és használatával elkülönítve hozhat létre Meter objektumokat.

Regisztrálja a IMeterFactory di-tárolót az alapértelmezett mérő-előállító implementációval:

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

A fogyasztók ezután beszerezhetik a mérő-előállítót, és felhasználhatják egy új Meter objektum létrehozásához.

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

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

Meter meter = meterFactory.Create(options);

MetricCollector<T> osztály

Az új MetricCollector<T> osztály lehetővé teszi metrikamérések és időbélyegek rögzítését. Emellett az osztály rugalmasan használhat egy tetszőleges időszolgáltatót a pontos időbélyeg-létrehozáshoz.

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

A frissített System.Numerics.Tensors NuGet-csomag api-kat tartalmaz az új TensorPrimitives névtérben, amelyek támogatják a tensor-műveleteket. A tenzor primitívjei optimalizálják az adatigényes számítási feladatokat, például az AI-t és a gépi tanulást.

Az olyan AI-számítási feladatok, mint a szemantikai keresés és a lekéréses-kiterjesztett generáció (RAG) kibővítik a nagy nyelvi modellek, például a ChatGPT természetes nyelvi képességeit a vonatkozó adatokkal kiegészített kérésekkel. Ezekben a számítási feladatokban a vektorokon végzett műveletek – például a koszinusz hasonlósága a kérdés megválaszolásához leginkább releváns adatok megtalálásához – kulcsfontosságúak. A System.Numerics.Tensors.TensorPrimitives csomag API-kat biztosít a vektorműveletek számára, ami azt jelenti, hogy nem kell külső függőséget használnia, és nem kell saját implementációt írnia.

Ez a csomag lecseréli a System.Numerics.Tensors csomagot.

További információ: A .NET 8 RC 2 bejelentése blogbejegyzés.

Lásd még