Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a cikk a .NET 9 .NET kódtárainak új funkcióit ismerteti.
Base64Url
A Base64 egy kódolási séma, amely tetszőleges bájtokat fordít le 64 karakterből álló szöveggé. Ez egy gyakori módszer az adatok átvitelére, és már régóta támogatott különféle módszerekkel, például a következőkkel Convert.ToBase64String vagy Base64.DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32, Boolean). Az általa használt karakterek némelyike azonban kevésbé ideális bizonyos körülmények között, például lekérdezési sztringekben való használatra. A Base64-táblázatot alkotó 64 karakter tartalmazza a "+" és a "/" karaktert, amelyek mindegyike saját jelentéssel rendelkezik az URL-címekben. Ez a Base64Url-sémához vezetett, amely hasonló a Base64-hez, de kissé eltérő karakterkészletet használ, amely alkalmassá teszi az URL-környezetekben való használatra. .NET 9 tartalmazza az új Base64Url osztályt, amely számos hasznos és optimalizált metódust biztosít a kódolási és dekódolási műveletekhez Base64Url különböző adattípusokkal.
Az alábbi példa az új osztály használatát mutatja be.
ReadOnlySpan<byte> bytes = ...;
string encoded = Base64Url.EncodeToString(bytes);
Bináris formázó
.NET 9 eltávolítja BinaryFormatter a .NET futtatókörnyezetből. Az API-k továbbra is jelen vannak, de implementációik mindig kivételt jelentenek a projekt típusától függetlenül. További információ az eltávolításról és a lehetőségekről, ha érintett, tekintse meg a BinaryFormatter áttelepítési útmutatóját.
Gyűjtemények
A .NET gyűjteménytípusai a következő frissítéseket kapják a .NET 9-hez:
- Gyűjteménykeresések spanokkal
OrderedDictionary<TKey, TValue>- A PriorityQueue.Remove() metódussal frissítheti az üzenetsor egy elemének prioritását.
ReadOnlySet<T>
Gyűjteménykeresések spanokkal
A nagy teljesítményű kódban a spanokat gyakran használják a sztringek szükségtelen kiosztásának elkerülésére, és a Dictionary<TKey,TValue> és HashSet<T> típusú táblákat gyakran alkalmazzák gyorsítótárként. Nem létezik biztonságos, beépített mechanizmus a spanokkal végzett keresések elvégzésére az ilyen gyűjteménytípusokon. A C# 13 új allows ref struct funkciójával és a 9..NET 9-ben található gyűjteménytípusok új funkcióival mostantól elvégezheti az ilyen típusú kereséseket.
Az alábbi példa a Dictionary<TKey,TValue>.GetAlternateLookup használatát mutatja be.
static Dictionary<string, int> CountWords(ReadOnlySpan<char> input)
{
Dictionary<string, int> wordCounts = new(StringComparer.OrdinalIgnoreCase);
Dictionary<string, int>.AlternateLookup<ReadOnlySpan<char>> spanLookup =
wordCounts.GetAlternateLookup<ReadOnlySpan<char>>();
foreach (Range wordRange in Regex.EnumerateSplits(input, @"\b\W+"))
{
if (wordRange.Start.Value == wordRange.End.Value)
{
continue; // Skip empty ranges.
}
ReadOnlySpan<char> word = input[wordRange];
spanLookup[word] = spanLookup.TryGetValue(word, out int count) ? count + 1 : 1;
}
return wordCounts;
}
OrderedDictionary<TKey, TValue>
Sok esetben érdemes lehet úgy tárolni a kulcs-érték párokat, hogy a sorrend megtartható legyen (a kulcs-érték párok listája), de ahol a kulcs szerinti gyors keresés is támogatott (a kulcs-érték párok szótára). A .NET korai napjai óta a OrderedDictionary típus támogatta ezt a forgatókönyvet, de csak nem általános módon, object típusú kulcsokkal és értékekkel. .NET 9 bevezeti a régóta kért OrderedDictionary<TKey,TValue> gyűjteményt, amely hatékony, általános típust biztosít ezeknek a forgatókönyveknek a támogatásához.
Az alábbi kód az új osztályt használja.
OrderedDictionary<string, int> d = new()
{
["a"] = 1,
["b"] = 2,
["c"] = 3,
};
d.Add("d", 4);
d.RemoveAt(0);
d.RemoveAt(2);
d.Insert(0, "e", 5);
foreach (KeyValuePair<string, int> entry in d)
{
Console.WriteLine(entry);
}
// Output:
// [e, 5]
// [b, 2]
// [c, 3]
PriorityQueue.Remove() metódus
.NET 6 bevezette a PriorityQueue<TElement,TPriority> gyűjteményt, amely egyszerű és gyors tömb-halom implementációt biztosít. A tömbök halmaival kapcsolatos egyik probléma általában az, hogy nem támogatják a prioritási frissítéseket, ami tiltó erejűvé teszi őket olyan algoritmusokban, mint a Dijkstra algoritmusának változatai.
Bár nem lehet hatékony $O(\log n)$ prioritási frissítéseket implementálni a meglévő gyűjteményben, az új PriorityQueue<TElement,TPriority>.Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) módszer lehetővé teszi a prioritási frissítések emulálását (bár $O(n)$ időpontban):
public static void UpdatePriority<TElement, TPriority>(
this PriorityQueue<TElement, TPriority> queue,
TElement element,
TPriority priority
)
{
// Scan the heap for entries matching the current element.
queue.Remove(element, out _, out _);
// Re-insert the entry with the new priority.
queue.Enqueue(element, priority);
}
Ez a módszer feloldja azokat a felhasználókat, akik gráf algoritmusokat szeretnének implementálni olyan környezetekben, ahol az aszimptotikus teljesítmény nem blokkoló. (Ilyen kontextus például az oktatás és a prototípus-készítés.) Itt látható például a Dijkstra új API-t használó algoritmusának egy toy implementálása .
ReadOnlySet<T>
Gyakran kívánatos a gyűjtemények írásvédett nézeteinek átadása. ReadOnlyCollection<T> Lehetővé teszi, hogy írásvédett burkolót hozzon létre egy tetszőleges mutable IList<T>körül, és ReadOnlyDictionary<TKey,TValue> lehetővé teszi, hogy írásvédett burkolót hozzon létre egy tetszőleges mutable IDictionary<TKey,TValue>körül. A .NET korábbi verziói azonban nem rendelkeztek beépített támogatással ugyanarra vonatkozóan a ISet<T>. .NET 9 bevezeti ReadOnlySet<T> a probléma megoldásához.
Az új osztály a következő használati mintát teszi lehetővé.
private readonly HashSet<int> _set = [];
private ReadOnlySet<int>? _setWrapper;
public ReadOnlySet<int> Set => _setWrapper ??= new(_set);
Összetevőmodell – TypeDescriptor trimmelés támogatása
System.ComponentModel új, opt-in trimmer-kompatibilis API-kat tartalmaz az összetevők leírásához. A vágási forgatókönyvek támogatásához ezek az új API-k bármely alkalmazás, különösen az önállóan futtatható, levágott alkalmazások esetében használhatók.
Az elsődleges API a TypeDescriptor.RegisterType metódus az TypeDescriptor osztályban. Ez a metódus rendelkezik az DynamicallyAccessedMembersAttribute attribútummal, így a vágó megőrzi az adott típushoz tartozó tagokat. Ezt a metódust típusonként egyszer kell meghívnia, és általában a folyamat elején.
A másodlagos API-knak van egy FromRegisteredType utótaga, például TypeDescriptor.GetPropertiesFromRegisteredType(Type). Az utótagot nem tartalmazó FromRegisteredType megfelelőiktől eltérően ezek az API-k nem rendelkeznek [RequiresUnreferencedCode] vagy [DynamicallyAccessedMembers] daraboló attribútumokkal. A vágóattribútumok hiánya segíti a fogyasztókat azáltal, hogy nem szükséges többé:
- Tiltsa le a vágási figyelmeztetéseket, ami kockázatos lehet.
- Terjessze ki a szigorúan típusos
Typeparamétert más metódusokba, ami nehezen kezelhető vagy kivitelezhetetlen lehet.
public static void RunIt()
{
// The Type from typeof() is passed to a different method.
// The trimmer doesn't know about ExampleClass anymore
// and thus there will be warnings when trimming.
Test(typeof(ExampleClass));
Console.ReadLine();
}
private static void Test(Type type)
{
// When publishing self-contained + trimmed,
// this line produces warnings IL2026 and IL2067.
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(type);
// When publishing self-contained + trimmed,
// the property count is 0 here instead of 2.
Console.WriteLine($"Property count: {properties.Count}");
// To avoid the warning and ensure reflection
// can see the properties, register the type:
TypeDescriptor.RegisterType<ExampleClass>();
// Get properties from the registered type.
properties = TypeDescriptor.GetPropertiesFromRegisteredType(type);
Console.WriteLine($"Property count: {properties.Count}");
}
public class ExampleClass
{
public string? Property1 { get; set; }
public int Property2 { get; set; }
}
További információ: API-javaslat.
Kriptográfia
- CryptographicOperations.HashData() metódus
- KMAC-algoritmus
- AES-GCM és ChaChaPoly1305 algoritmusok engedélyezettek az iOS/tvOS/MacCatalyst rendszerhez
- X.509-tanúsítvány betöltése
- OpenSSL-szolgáltatók támogatása
- Windows CNG virtualizáláson alapuló biztonság
CryptographicOperations.HashData() metódus
.NET több statikus "egylövetű" kivonatfüggvények és kapcsolódó függvények implementációit tartalmazza. Ezek az API-k magukban foglalják SHA256.HashData és HMACSHA256.HashData. Az egyszer használatos API-k használata előnyösebb, mert a lehető legjobb teljesítményt nyújtják, és csökkenthetik vagy kiküszöbölhetik az erőforrás-allokációkat.
Ha egy fejlesztő olyan API-t szeretne biztosítani, amely támogatja a kivonatolást, ahol a hívó határozza meg a használni kívánt kivonatoló algoritmust, az általában egy HashAlgorithmName argumentum elfogadásával történik. Ha azonban ezt a mintát egylövetű API-kkal használja, minden lehetséges HashAlgorithmName lehetőséget át kell váltania, majd a megfelelő módszert kell használnia. A probléma megoldásához a .NET 9 bevezeti a CryptographicOperations.HashData API-t. Ez az API lehetővé teszi, hogy egy bemeneten egyszeri kivonatot vagy HMAC-t hozzon létre, ahol a használt algoritmust egy HashAlgorithmName határozza meg.
static void HashAndProcessData(HashAlgorithmName hashAlgorithmName, byte[] data)
{
byte[] hash = CryptographicOperations.HashData(hashAlgorithmName, data);
ProcessHash(hash);
}
KMAC-algoritmus
.NET 9 a NIST SP-800-185 által megadott KMAC-algoritmust biztosítja. A KECCAK üzenethitelesítési kód (KMAC) egy pszeudorandom függvény és a KECCAK-on alapuló kulcsos kivonatoló függvény.
Az alábbi új osztályok a KMAC-algoritmust használják. A példányok használatával adatokat halmozhat fel egy MAC (üzenet hitelesítési kód) létrehozásához, vagy használhatja a statikus HashData metódust egyetlen bemenet egyszeri feldolgozására.
A KMAC Linuxon az OpenSSL 3.0-s vagy újabb verziójával, valamint Windows 11 26016-os vagy újabb builden érhető el. A statikus IsSupported tulajdonság használatával megállapíthatja, hogy a platform támogatja-e a kívánt algoritmust.
if (Kmac128.IsSupported)
{
byte[] key = GetKmacKey();
byte[] input = GetInputToMac();
byte[] mac = Kmac128.HashData(key, input, outputLength: 32);
}
else
{
// Handle scenario where KMAC isn't available.
}
AES-GCM és ChaChaPoly1305 algoritmusok engedélyezve az iOS/tvOS/MacCatalyst rendszerhez
IsSupported és ChaChaPoly1305.IsSupported most igaz értéket ad vissza iOS 13+, tvOS 13+ és Mac Catalyst rendszereken való futtatáskor.
AesGcm csak a 16 bájtos (128 bites) címkeértékeket támogatja az Apple operációs rendszereken.
X.509-tanúsítvány betöltése
A .NET Framework 2.0 óta a tanúsítvány betöltésének módja new X509Certificate2(bytes). Voltak más minták is, például new X509Certificate2(bytes, password, flags), new X509Certificate2(path), new X509Certificate2(path, password, flags), és X509Certificate2Collection.Import(bytes, password, flags) (túlterheléseivel együtt).
Ezek a metódusok mind tartalomszimatolást használtak annak megállapítására, hogy a bemenetet kezelni tudták-e, majd betöltötték, ha kezelhető volt. Egyes hívók számára ez a stratégia nagyon kényelmes volt. De van néhány problémája is:
- Nem minden fájlformátum működik minden operációs rendszeren.
- Ez egy protokolleltérés.
- Ez biztonsági problémák forrása.
.NET 9 egy új X509CertificateLoader osztályt vezet be, amely egy "egyetlen módszer, egyetlen cél" kialakítással rendelkezik. A kezdeti verzióban a konstruktor által támogatott öt formátum X509Certificate2 közül csak kettőt támogat. Ez a két formátum működött az összes műveleti rendszeren.
OpenSSL-szolgáltatók támogatása
.NET 8 bevezette az OpenSSL-specifikus API-kat OpenPrivateKeyFromEngine(String, String) és OpenPublicKeyFromEngine(String, String). Lehetővé teszik az OpenSSL-összetevőkkel ENGINE való interakciót, és például hardveres biztonsági modulokat (HSM) használnak.
.NET 9 bevezeti a SafeEvpPKeyHandle.OpenKeyFromProvider(String, String), amely lehetővé teszi OpenSSL-szolgáltatók használatát, valamint olyan szolgáltatókkal való interakciót, mint tpm2 vagy pkcs11.
Egyes disztribúciók eltávolították ENGINE a támogatást, mivel az már elavult.
Az alábbi kódrészlet az alapszintű használatot mutatja be:
byte[] data = [ /* example data */ ];
// Refer to your provider documentation, for example, https://github.com/tpm2-software/tpm2-openssl/tree/master.
using (SafeEvpPKeyHandle priKeyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("tpm2", "handle:0x81000007"))
using (ECDsa ecdsaPri = new ECDsaOpenSsl(priKeyHandle))
{
byte[] signature = ecdsaPri.SignData(data, HashAlgorithmName.SHA256);
// Do stuff with signature created by TPM.
}
A TLS-kézfogás során néhány teljesítménybeli fejlesztés történt, és javultak az RSA titkos kulcsokkal való interakciók, amelyek ENGINE összetevőket használnak.
Windows CNG virtualizáláson alapuló biztonság
Windows 11 új API-kat adott hozzá a Windows kulcsok virtualizáció-alapú biztonsággal (VBS) való védelméhez. Ezzel az új funkcióval a kulcsok védhetők a rendszergazdai szintű kulcslopásos támadásoktól, és elhanyagolható hatással lehetnek a teljesítményre, a megbízhatóságra és a skálázásra.
.NET 9 egyező CngKeyCreationOptions jelzőket adott hozzá. A következő három jelző lett hozzáadva:
-
CngKeyCreationOptions.PreferVbsmegfelelőNCRYPT_PREFER_VBS_FLAG -
CngKeyCreationOptions.RequireVbsmegfelelőNCRYPT_REQUIRE_VBS_FLAG -
CngKeyCreationOptions.UsePerBootKeymegfelelőNCRYPT_USE_PER_BOOT_KEY_FLAG
Az alábbi kódrészlet bemutatja, hogyan használhatja az egyik jelzőt:
using System.Security.Cryptography;
CngKeyCreationParameters cngCreationParams = new()
{
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
KeyCreationOptions = CngKeyCreationOptions.RequireVbs | CngKeyCreationOptions.OverwriteExistingKey,
};
using (CngKey key = CngKey.Create(CngAlgorithm.ECDsaP256, "myKey", cngCreationParams))
using (ECDsaCng ecdsa = new ECDsaCng(key))
{
// Do stuff with the key.
}
Dátum és idő – új TimeSpan.From* túlterhelések
Az TimeSpan osztály számos From* módszert kínál, amelyekkel egy TimeSpan segítségével double objektumot hozhat létre. Mivel double azonban bináris alapú lebegőpontos formátumról van szó, az eredendő pontatlanság hibákhoz vezethet. Például, lehet, hogy TimeSpan.FromSeconds(101.832) nem pontosan 101 seconds, 832 milliseconds, hanem inkább hozzávetőlegesen 101 seconds, 831.9999999999936335370875895023345947265625 milliseconds. Ez az eltérés gyakori zavart okozott, és nem ez a leghatékonyabb módja az ilyen adatok ábrázolásának. Ennek megoldásához .NET 9 új túlterheléseket ad hozzá, amelyekkel TimeSpan objektumokat hozhat létre egész számokból. Új túlterhelések vannak a FromDays, FromHours, FromMinutes, FromSeconds, FromMillisecondsés FromMicroseconds.
Az alábbi kód egy példát mutat be az double és az új egész szám túlterhelések egyikének meghívására.
TimeSpan timeSpan1 = TimeSpan.FromSeconds(value: 101.832);
Console.WriteLine($"timeSpan1 = {timeSpan1}");
// timeSpan1 = 00:01:41.8319999
TimeSpan timeSpan2 = TimeSpan.FromSeconds(seconds: 101, milliseconds: 832);
Console.WriteLine($"timeSpan2 = {timeSpan2}");
// timeSpan2 = 00:01:41.8320000
Függőséginjektálás – ActivatorUtilities.CreateInstance konstruktor
A ActivatorUtilities.CreateInstance konstruktorfelbontása a .NET 9-ben módosult. Korábban előfordulhat, hogy a konstruktorok sorrendjétől és a ActivatorUtilitiesConstructorAttribute konstruktorparaméterek számától függően az attribútum használatával explicit módon megjelölt konstruktor nem hívható meg. A logika megváltozott a .NET 9-ben, így az attribútumot tartalmazó konstruktort mindig meghívják.
Diagnosztika
- A Debug.Assert alapértelmezés szerint lejelenti az állapot ellenőrzését
- New Activity.AddLink metódus
- Metrics.Gauge műszer
- Out-of-proc Meter helyettesítő karakter figyelése
A Debug.Assert alapértelmezés szerint jelenti az állítás feltételét.
Debug.Assert gyakran használják az olyan feltételek ellenőrzésére, amelyek várhatóan mindig igazak lesznek. A hiba általában hibát jelez a kódban. Sok túlterhelés van Debug.Assert, amelyek közül a legegyszerűbb csak egy feltételt fogad el:
Debug.Assert(a > 0 && b > 0);
Az állítás meghiúsul, ha a feltétel hamis. Korábban azonban az ilyen kijelentések nem tartalmaztak semmilyen információt arra vonatkozóan, hogy mely feltétel teljesült, vagy nem. A .NET 9-től kezdődően, ha a felhasználó nem ad meg explicit üzenetet, az állítás tartalmazza a feltétel szöveges ábrázolását. Például az előző állítási példához, ahelyett, hogy a következőhöz hasonló üzenetet kapnál:
Process terminated. Assertion failed.
at Program.SomeMethod(Int32 a, Int32 b)
Az üzenet ekkor a következő lesz:
Process terminated. Assertion failed.
a > 0 && b > 0
at Program.SomeMethod(Int32 a, Int32 b)
Új Activity.AddLink metódus
Korábban csak más nyomkövetési környezetekhez kapcsolhattál egy nyomkövetést Activity, amikor létrehoztad a Activity. A .NET 9-ben új AddLink(ActivityLink) API lehetővé teszi egy Activity objektum más nyomkövetési környezetekhez való csatolását a létrehozás után. Ez a módosítás az OpenTelemetria specifikációihoz is igazodik .
ActivityContext activityContext = new(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
ActivityLink activityLink = new(activityContext);
Activity activity = new("LinkTest");
activity.AddLink(activityLink);
Metrics.Gauge műszer
System.Diagnostics.Metrics mostantól az Gauge<T> OpenTelemetry specifikációjának megfelelően biztosítja a műszert. A Gauge műszer nem additív értékek rögzítésére szolgál, ha változások történnek. Például meg tudja mérni a háttérzaj szintjét, ahol a több helyiség értékeinek összeadása értelmetlen lenne. Az Gauge eszköz egy általános típus, amely bármilyen értéktípust rögzíthet, például int: , doublevagy decimal.
Az alábbi példa a Gauge műszer használatát mutatja be.
Meter soundMeter = new("MeasurementLibrary.Sound");
Gauge<int> gauge = soundMeter.CreateGauge<int>(
name: "NoiseLevel",
unit: "dB", // Decibels.
description: "Background Noise Level"
);
gauge.Record(10, new TagList() { { "Room1", "dB" } });
Out-of-proc Meter helyettesítő szimbólum figyelése
A mérőket már lehet figyelni a System.Diagnostics.Metrics eseményforrás-szolgáltató használatával, de a .NET 9 előtt meg kellett adnia a teljes mérő nevét. A .NET 9-ben figyelhet az összes mérőre a * helyettesítő karakter segítségével, amely lehetővé teszi a metrikák gyűjtését egy folyamat összes mérőjéről. Emellett támogatja a fogyasztásmérő-előtagok figyelését is, így minden olyan mérőt meghallgathat, amelynek a neve egy megadott előtaggal kezdődik. Például a MyMeter* megadásával lehetővé válik az összes olyan mérő figyelése, amelynek neve MyMeter-vel kezdődik.
// The complete meter name is "MyCompany.MyMeter".
var meter = new Meter("MyCompany.MyMeter");
// Create a counter and allow publishing values.
meter.CreateObservableCounter("MyCounter", () => 1);
// Create the listener to use the wildcard character
// to listen to all meters using prefix names.
MyEventListener listener = new MyEventListener();
Az MyEventListener osztály a következőképpen van definiálva.
internal class MyEventListener : EventListener
{
protected override void OnEventSourceCreated(EventSource eventSource)
{
Console.WriteLine(eventSource.Name);
if (eventSource.Name == "System.Diagnostics.Metrics")
{
// Listen to all meters with names starting with "MyCompany".
// If using "*", allow listening to all meters.
EnableEvents(
eventSource,
EventLevel.Informational,
(EventKeywords)0x3,
new Dictionary<string, string?>() { { "Metrics", "MyCompany*" } }
);
}
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
// Ignore other events.
if (eventData.EventSource.Name != "System.Diagnostics.Metrics" ||
eventData.EventName == "CollectionStart" ||
eventData.EventName == "CollectionStop" ||
eventData.EventName == "InstrumentPublished"
)
return;
Console.WriteLine(eventData.EventName);
if (eventData.Payload is not null)
{
for (int i = 0; i < eventData.Payload.Count; i++)
Console.WriteLine($"\t{eventData.PayloadNames![i]}: {eventData.Payload[i]}");
}
}
}
A kód végrehajtásakor a kimenet a következő:
CounterRateValuePublished
sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522
meterName: MyCompany.MyMeter
meterVersion:
instrumentName: MyCounter
unit:
tags:
rate: 0
value: 1
instrumentId: 1
CounterRateValuePublished
sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522
meterName: MyCompany.MyMeter
meterVersion:
instrumentName: MyCounter
unit:
tags:
rate: 0
value: 1
instrumentId: 1
A helyettesítő karakterrel figyelheti a metrikákat olyan monitorozási eszközökkel, mint a dotnet-counters.
LINQ
Új módszereket CountByAggregateBy vezetnek be. Ezek a metódusok lehetővé teszik az állapot kulcs szerinti összesítését anélkül, hogy köztes csoportosításokat kellene lefoglalni.GroupBy
CountBy segítségével gyorsan kiszámíthatja az egyes kulcsok gyakoriságát. Az alábbi példa megkeresi a szövegsztringben leggyakrabban előforduló szót.
string sourceText = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed non risus. Suspendisse lectus tortor, dignissim sit amet,
adipiscing nec, ultricies sed, dolor. Cras elementum ultrices amet diam.
""";
// Find the most frequent word in the text.
KeyValuePair<string, int> mostFrequentWord = sourceText
.Split(new char[] { ' ', '.', ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(word => word.ToLowerInvariant())
.CountBy(word => word)
.MaxBy(pair => pair.Value);
Console.WriteLine(mostFrequentWord.Key); // amet
AggregateBy lehetővé teszi általános célú munkafolyamatok implementálását. Az alábbi példa bemutatja, hogyan számíthatja ki az adott kulcshoz társított pontszámokat.
(string id, int score)[] data =
[
("0", 42),
("1", 5),
("2", 4),
("1", 10),
("0", 25),
];
var aggregatedData =
data.AggregateBy(
keySelector: entry => entry.id,
seed: 0,
(totalScore, curr) => totalScore + curr.score
);
foreach (var item in aggregatedData)
{
Console.WriteLine(item);
}
//(0, 67)
//(1, 15)
//(2, 4)
Index<TSource>(IEnumerable<TSource>) lehetővé teszi az enumerálás implicit indexének gyors kinyerését. Most már írhat kódot, például az alábbi kódrészletet a gyűjtemény elemeinek automatikus indexeléséhez.
IEnumerable<string> lines2 = File.ReadAllLines("output.txt");
foreach ((int index, string line) in lines2.Index())
{
Console.WriteLine($"Line number: {index + 1}, Line: {line}");
}
Naplózási forrásgenerátor
A C# 12 bevezette az elsődleges konstruktorokat, amelyekkel közvetlenül az osztálydeklarációban definiálhat konstruktort. A naplózási forrásgenerátor mostantól támogatja a naplózást elsődleges konstruktorral rendelkező osztályok használatával.
public partial class ClassWithPrimaryConstructor(ILogger logger)
{
[LoggerMessage(0, LogLevel.Debug, "Test.")]
public partial void Test();
}
Egyéb
Ebben a szakaszban a következő információk találhatók:
allows ref struct könyvtárakban használatos
A C# 13 lehetővé teszi egy általános paraméter allows ref structkorlátozását, amely tájékoztatja a fordítót és a futtatókörnyezetet arról, hogy az adott általános paraméterhez használható.ref struct Számos, ezzel kompatibilis API-t már megjelöltek. A String.Create metódusnak például van egy túlterhelése, amely lehetővé teszi, hogy úgy hozzon létre egy string objektumot, hogy közvetlenül a memóriájába ír, és ez spanként ábrázolódik. Ez a metódus egy TState argumentumot ad át a hívótól a tényleges írást végző delegáltnak.
Az TState típusparaméter mostantól a következő jelöléssel van ellátva: String.Create
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
where TState : allows ref struct;
Ez az annotáció lehetővé teszi, hogy a metódus bemeneteként adjon meg egy szakaszt (vagy bármely más ref struct).
Az alábbi példa egy új String.ToLowerInvariant() túlterhelést mutat be, amely ezt a képességet használja.
public static string ToLowerInvariant(ReadOnlySpan<char> input) =>
string.Create(span.Length, input, static (stringBuffer, input) => span.ToLowerInvariant(stringBuffer));
SearchValues bővítés
.NET 8 bevezette a SearchValues<T> típust, amely optimalizált megoldást kínál adott karakterkészletek vagy bájtok keresésére a spanokon belül. A .NET 9 SearchValues kibővítették, hogy támogassa a nagyobb sztringek alsztringjeinek keresését.
Az alábbi példa több állatnevet keres egy sztringértéken belül, és egy indexet ad vissza az első megtalálthoz.
private static readonly SearchValues<string> s_animals =
SearchValues.Create(["cat", "mouse", "dog", "dolphin"], StringComparison.OrdinalIgnoreCase);
public static int IndexOfAnimal(string text) =>
text.AsSpan().IndexOfAny(s_animals);
Ez az új funkció egy optimalizált implementációval rendelkezik, amely kihasználja a SIMD-támogatás előnyeit a mögöttes platformon. Emellett lehetővé teszi a magasabb szintű típusok optimalizálását is. Most például Regex ezt a funkciót használja az implementáció részeként.
hálózat
- A SocketsHttpHandler alapértelmezés szerint a HttpClientFactory-ban van
- System.Net.ServerSentEvents
- TLS átvitel-folytatás ügyféltanúsítványokkal Linuxon
- WebSocket–életben tartó pingelés és időtúllépés
- A HttpClientFactory alapértelmezés szerint nem naplózza a fejlécértékeket
A SocketsHttpHandler alapértelmezés szerint a HttpClientFactory-ban van
HttpClientFactory alapértelmezés szerint HttpClient objektumokat hoz létre, amelyeket HttpClientHandler támogat. A HttpClientHandler maga is háttértámogatást kap a SocketsHttpHandler-től, amely sokkal inkább konfigurálható, beleértve a kapcsolat élettartamának kezelését is.
HttpClientFactory Mostantól alapértelmezés szerint használja SocketsHttpHandler , és úgy konfigurálja, hogy a kapcsolat élettartamára vonatkozó korlátokat állítsa be a gyárban megadott rotációs élettartamnak megfelelően.
System.Net.ServerSentEvents
A kiszolgáló által küldött események (SSE) egy egyszerű és népszerű protokoll az adatok kiszolgálóról ügyfélre való átviteléhez. Az OpenAI például az AI-szolgáltatásokból létrehozott szöveg streamelésének részeként használja. Az SSE felhasználásának egyszerűsítése érdekében az új System.Net.ServerSentEvents kódtár elemzőt biztosít a kiszolgáló által küldött események egyszerű betöltéséhez.
Az alábbi kód bemutatja az új osztály használatát.
Stream responseStream = new MemoryStream();
await foreach (SseItem<string> e in SseParser.Create(responseStream).EnumerateAsync())
{
Console.WriteLine(e.Data);
}
TLS újraindítás ügyféltanúsítványokkal Linuxon
A TLS-folytatás a TLS protokoll egyik funkciója, amely lehetővé teszi a korábban létrehozott munkamenetek kiszolgálóra történő újrakezdését. Ezzel elkerülhet néhány oda-vissza utat, és számítási erőforrásokat takarít meg a TLS-kézfogás során.
A TLS resume már támogatott a Linuxon az ügyféltanúsítványok nélküli SslStream kapcsolatok esetében. .NET 9 támogatja a kölcsönösen hitelesített TLS-kapcsolatok újraindítását, amelyek gyakoriak a kiszolgáló-kiszolgáló forgatókönyvekben. A funkció automatikusan engedélyezve van.
WebSocket kapcsolat-életben tartás és időkifutási beállítás
Új API-k a ClientWebSocketOptions és a WebSocketCreationOptions rendszeren lehetővé teszik, hogy választhassuk a WebSocket pingek küldését, és megszakítsuk a kapcsolatot, ha a társ nem válaszol időben.
Eddig megadhatta a KeepAliveInterval-t, hogy a kapcsolat ne maradjon inaktív, de nem volt beépített mechanizmus annak biztosítására, hogy a társ válaszoljon.
Az alábbi példa 5 másodpercenként pingeli a kiszolgálót, és megszakítja a kapcsolatot, ha egy másodpercen belül nem válaszol.
using var cws = new ClientWebSocket();
cws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
cws.Options.KeepAliveInterval = TimeSpan.FromSeconds(5);
cws.Options.KeepAliveTimeout = TimeSpan.FromSeconds(1);
await cws.ConnectAsync(uri, httpClient, cancellationToken);
A HttpClientFactory alapértelmezés szerint nem naplózza a fejlécértékeket
LogLevel.Trace a HttpClientFactory naplózott események alapértelmezés szerint nem tartalmazzák a fejlécértékeket. Az egyes fejlécek értékeit a RedactLoggedHeaders segédmetóduson keresztül naplózhatja.
Az alábbi példa az összes fejlécet kitakarja, kivéve a felhasználói ügynököt.
services.AddHttpClient("myClient")
.RedactLoggedHeaders(name => name != "User-Agent");
Az alapértelmezés szerint a HttpClientFactory naplózása kitakarja a fejlécértékeket.
Elmélkedés
Tárolt egységek
A .NET Core-verziókban és a .NET 5-8-ban a dinamikusan létrehozott típusok szerelvényeinek és tükrözési metaadatainak kiadásának támogatása korlátozott a futtatható AssemblyBuilder. Az összeállítás mentési támogatásának hiánya gyakran akadályt jelentett a .NET Keretrendszerről .NET-re áttelepített ügyfelek számára. .NET 9 hozzáad egy új típust, PersistedAssemblyBuilder, amellyel mentheti a kibocsátott assemblyt.
Példány létrehozásához PersistedAssemblyBuilder hívja meg a konstruktort, és adja meg a szerelvény nevét, az alapvető szerelvényt, System.Private.CoreLibhogy hivatkozzon az alap futtatókörnyezet típusaira és az opcionális egyéni attribútumokra. Miután kibocsátotta az összes tagot a szerelvénybe, hívja meg a PersistedAssemblyBuilder.Save(String) metódust, hogy hozzon létre egy alapértelmezett beállításokkal rendelkező szerelvényt. Ha meg szeretné adni a belépési pontot vagy más beállításokat, meghívhatja PersistedAssemblyBuilder.GenerateMetadata és használhatja a visszaadott metaadatokat a szerelvény mentéséhez. Az alábbi kód azt mutatja be, hogyan lehet létrehozni egy tartós assembly-t és beállítani a belépési pontot.
public void CreateAndSaveAssembly(string assemblyPath)
{
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(
new AssemblyName("MyAssembly"),
typeof(object).Assembly
);
TypeBuilder tb = ab.DefineDynamicModule("MyModule")
.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder entryPoint = tb.DefineMethod(
"Main",
MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static
);
ILGenerator il = entryPoint.GetILGenerator();
// ...
il.Emit(OpCodes.Ret);
tb.CreateType();
MetadataBuilder metadataBuilder = ab.GenerateMetadata(
out BlobBuilder ilStream,
out BlobBuilder fieldData
);
PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(
imageCharacteristics: Characteristics.ExecutableImage);
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
header: peHeaderBuilder,
metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
ilStream: ilStream,
mappedFieldData: fieldData,
entryPoint: MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken)
);
BlobBuilder peBlob = new BlobBuilder();
peBuilder.Serialize(peBlob);
using var fileStream = new FileStream("MyAssembly.exe", FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(fileStream);
}
public static void UseAssembly(string assemblyPath)
{
Assembly assembly = Assembly.LoadFrom(assemblyPath);
Type? type = assembly.GetType("MyType");
MethodInfo? method = type?.GetMethod("SumMethod");
Console.WriteLine(method?.Invoke(null, [5, 10]));
}
Az új PersistedAssemblyBuilder osztály tartalmazza a PDB-támogatást. Szimbólumadatokat bocsáthat ki, és használatával hibakeresést végezhet egy létrehozott szerelvényben. Az API a .NET-keretrendszer implementálásához hasonló alakú. További információ: Szimbólumok kibocsátása és PDF-fájl létrehozása.
Típusnév elemzése
TypeName az ECMA-335 típusnevek egy olyan elemzője, amely hasonló funkciókat biztosít, mint a System.Type, de el van választva a futtatókörnyezettől. Az olyan összetevőknek, mint a szerializálók és a fordítók, elemezni és feldolgozni kell a típusneveket. Például a natív AOT-fordító már a TypeName használatára váltott.
Az új TypeName osztály a következőt biztosítja:
Statikus
ParseésTryParsemetódusok a bemenetReadOnlySpan<char>elemzéséhez. Mindkét metódus elfogad egy osztálypéldánytTypeNameParseOptions(egy lehetőségcsomagot), amely lehetővé teszi az elemzés testreszabását.Name,FullNameésAssemblyQualifiedNameazok a tulajdonságok, amelyek pontosan úgy működnek, mint a megfelelőik a System.Type.Több tulajdonság és metódus, amelyek további információt nyújtanak magáról a névről:
-
IsArray,IsSZArray(SZegydimenziós, nulla indexelt tömböt jelent),IsVariableBoundArrayTypeésGetArrayRanktömbökkel való munkavégzéshez. -
IsConstructedGenericType,GetGenericTypeDefinitionésGetGenericArgumentsáltalános típusnevek használata esetén. -
IsByRefésIsPointera mutatókkal és a felügyelt hivatkozásokkal való munkához. -
GetElementType()mutatókkal, hivatkozásokkal és tömbökkel végzett munkához. -
IsNestedésDeclaringTypea beágyazott típusok használata érdekében. -
AssemblyName, amely az új AssemblyNameInfo osztályon keresztül teszi elérhetővé a szerelvénynévadatokat. EllentétbenAssemblyName-val, az új típus módosíthatatlan, és a kulturális nevek elemzése nem hoz létre példányokatCultureInfo.
-
Mindkettő TypeName és AssemblyNameInfo típus nem módosítható, és nem biztosít módot az egyenlőség ellenőrzésére (nem implementálják IEquatable). A szerelvénynevek összehasonlítása egyszerű, de a különböző forgatókönyveknek csak a közzétett információk (Name, , Versionés CultureNamePublicKeyOrToken) egy részhalmazát kell összehasonlítaniuk.
Az alábbi kódrészlet néhány példahasználatot mutat be.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
internal class RestrictedSerializationBinder
{
Dictionary<string, Type> AllowList { get; set; }
RestrictedSerializationBinder(Type[] allowedTypes)
=> AllowList = allowedTypes.ToDictionary(type => type.FullName!);
Type? GetType(ReadOnlySpan<char> untrustedInput)
{
if (!TypeName.TryParse(untrustedInput, out TypeName? parsed))
{
throw new InvalidOperationException($"Invalid type name: '{untrustedInput.ToString()}'");
}
if (AllowList.TryGetValue(parsed.FullName, out Type? type))
{
return type;
}
else if (parsed.IsSimple // It's not generic, pointer, reference, or an array.
&& parsed.AssemblyName is not null
&& parsed.AssemblyName.Name == "MyTrustedAssembly"
)
{
return Type.GetType(parsed.AssemblyQualifiedName, throwOnError: true);
}
throw new InvalidOperationException($"Not allowed: '{untrustedInput.ToString()}'");
}
}
Az új API-k a System.Reflection.Metadata NuGet csomagból érhetők el, amelyek alacsonyabb szintű .NET verziókkal használhatók.
Reguláris kifejezések
[GeneratedRegex] tulajdonságokon
.NET 7 bevezette a Regex forrásgenerátort és a megfelelő GeneratedRegexAttribute attribútumot.
A következő részleges metódus lesz létrehozva az ehhez Regexszükséges kóddal együtt.
[GeneratedRegex(@"\b\w{5}\b")]
private static partial Regex FiveCharWord();
A C# 13 támogatja a részleges properties a részleges metódusok mellett, így a 9. .NET-tól kezdve [GeneratedRegex(...)] is használhat egy tulajdonságon.
Az alábbi részleges tulajdonság az előző példának megfelelő tulajdonság.
[GeneratedRegex(@"\b\w{5}\b")]
private static partial Regex FiveCharWordProperty { get; }
Regex.EnumerateSplits
Az Regex osztály egy Split metódust biztosít, amely koncepciójában hasonló a String.Split metódushoz. Ezzel String.Splitegy vagy több charstring elválasztót ad meg, és az implementáció felosztja a bemeneti szöveget ezeken az elválasztókon. Ahelyett, hogy az elválasztót Regex.Split vagy char alakban adná meg, itt normál kifejezési mintaként van megadva.
Az alábbi példa bemutatja.Regex.Split
foreach (string s in Regex.Split("Hello, world! How are you?", "[aeiou]"))
{
Console.WriteLine($"Split: \"{s}\"");
}
// Output, split by all English vowels:
// Split: "H"
// Split: "ll"
// Split: ", w"
// Split: "rld! H"
// Split: "w "
// Split: "r"
// Split: " y"
// Split: ""
// Split: "?"
Azonban a Regex.Split csak string-ként fogadja el, és nem támogatja, hogy bemenetként ReadOnlySpan<char>-ként legyen megadva. Emellett a teljes felosztáskészletet string[] formájában adja ki, amihez ki kell osztani az string tömböt az eredmények tárolásához, és egy string tömböt minden egyes felosztáshoz. A .NET 9 új EnumerateSplits metódusa lehetővé teszi ugyanannak a műveletnek a végrehajtását, de spán alapú bemenettel és az eredmények lefoglalása nélkül. Elfogad egy ReadOnlySpan<char>-t, és visszaad egy Range objektumokat tartalmazó felsorolást, amelyek az eredményeket képviselik.
Az alábbi példa bemutatja Regex.EnumerateSplits, amely bemenetként egy ReadOnlySpan<char> értéket vesz.
ReadOnlySpan<char> input = "Hello, world! How are you?";
foreach (Range r in Regex.EnumerateSplits(input, "[aeiou]"))
{
Console.WriteLine($"Split: \"{input[r]}\"");
}
Szerializálás (System.Text.Json)
- Behúzási beállítások
- Alapértelmezett webes beállítások önállóan
- JsonSchemaExporter
- Null értékű széljegyzetek tiszteletben tartása
- Nem választható konstruktorparaméterek megkövetelése
- A JsonObject tulajdonságainak sorrendje
- Az enum tagok nevének testreszabása
- Több JSON-dokumentum streamelése
Behúzási beállítások
JsonSerializerOptions új tulajdonságokat tartalmaz, amelyekkel testre szabhatja az írott JSON behúzási karakterét és behúzási méretét.
var options = new JsonSerializerOptions
{
WriteIndented = true,
IndentCharacter = '\t',
IndentSize = 2,
};
string json = JsonSerializer.Serialize(
new { Value = 1 },
options
);
Console.WriteLine(json);
//{
// "Value": 1
//}
Alapértelmezett webes beállítások önállóan
Ha a ASP.NET Core által használt default beállításokkal szeretne szerializálni webalkalmazásokhoz, használja az új JsonSerializerOptions.Web singletont.
string webJson = JsonSerializer.Serialize(
new { SomeValue = 42 },
JsonSerializerOptions.Web // Defaults to camelCase naming policy.
);
Console.WriteLine(webJson);
// {"someValue":42}
JSON séma exportáló
A JSON-t gyakran használják a metódus-aláírások típusainak távoli eljáráshívási sémák részeként történő ábrázolására. Például az OpenAPI-specifikációk részeként, vagy olyan AI-szolgáltatásokkal való eszközhívás részeként használják, mint amilyenek az OpenAI-ból származók. A fejlesztők JSON-ként szerializálhatják és deszerializálhatják .NET típusokat System.Text.Json használatával. De olyan JSON-sémát is be kell szerezniük, amely leírja a .NET típus alakját (azaz leírja, hogy mi szerializálható, és mit lehet deszerializálni). System.Text.Json mostantól a JsonSchemaExporter típust biztosítja, amely támogatja a .NET típust képviselő JSON-séma létrehozásához.
További információ: JSON-sémaexportáló.
Nullálható annotációk tiszteletben tartása
System.Text.Json mostantól felismeri a tulajdonságok nullbilitási jelöléseit, és úgy konfigurálható, hogy a RespectNullableAnnotations zászlóval kényszerítse ezeket a szerializálás és deszerializálás során.
A következő kód bemutatja, hogyan állíthatja be a beállítást:
public static void RunIt()
{
JsonSerializerOptions options = new() { RespectNullableAnnotations = true };
// Throws exception: System.Text.Json.JsonException: The property or field
// 'Title' on type 'Serialization+Book' doesn't allow getting null values.
// Consider updating its nullability annotation.
JsonSerializer.Serialize(new Book { Title = null! }, options);
// Throws exception: System.Text.Json.JsonException: The property or field
// 'Title' on type 'Serialization+Book' doesn't allow setting null values.
// Consider updating its nullability annotation.
JsonSerializer.Deserialize<Book>("""{ "Title" : null }""", options);
}
public class Book
{
public required string Title { get; set; }
public string? Author { get; set; }
public int PublishYear { get; set; }
}
További információ: Null értékű széljegyzetek tiszteletben tartása.
Nem választható konstruktorparaméterek megkövetelése
System.Text.Json Korábban nem választható konstruktorparamétereket nem kötelezőként kezelt a konstruktoralapú deszerializálás használatakor. Ezt a viselkedést az új RespectRequiredConstructorParameters jelzővel módosíthatja.
A következő kód bemutatja, hogyan állíthatja be a beállítást:
JsonSerializerOptions options = new() { RespectRequiredConstructorParameters = true };
// Throws exception: System.Text.Json.JsonException: JSON deserialization
// for type 'Serialization+MyPoco' was missing required properties including: 'Value'.
JsonSerializer.Deserialize<MyPoco>("""{}""", options);
A MyPoco típus a következőképpen van definiálva:
record MyPoco(string Value);
További információ: Nem választható konstruktorparaméterek.
A JsonObject tulajdonságainak sorrendje
A JsonObject típus mostantól rendezett szótárhoz hasonló API-kat kínál, amelyek lehetővé teszik az explicit tulajdonságrendek kezelését.
JsonObject jObj = new()
{
["key1"] = true,
["key3"] = 3
};
Console.WriteLine(jObj is IList<KeyValuePair<string, JsonNode?>>); // True.
// Insert a new key-value pair at the correct position.
int key3Pos = jObj.IndexOf("key3") is int i and >= 0 ? i : 0;
jObj.Insert(key3Pos, "key2", "two");
foreach (KeyValuePair<string, JsonNode?> item in jObj)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
// Output:
// key1: true
// key2: two
// key3: 3
További információ: Tulajdonságrend módosítása.
Az enumerálási tagok nevének testreszabása
Az új System.Text.Json.Serialization.JsonStringEnumMemberNameAttribute attribútum a sztringként szerializált típusok egyes számtagjainak nevének testreszabására használható:
JsonSerializer.Serialize(MyEnum.Value1 | MyEnum.Value2); // "Value1, Custom enum value"
[Flags, JsonConverter(typeof(JsonStringEnumConverter))]
enum MyEnum
{
Value1 = 1,
[JsonStringEnumMemberName("Custom enum value")]
Value2 = 2,
}
További információt az alábbi linken talál: Egyéni felsorolási tagnevek.
Több JSON-dokumentum streamelése
System.Text.Json.Utf8JsonReader Mostantól támogatja több, szóközrel elválasztott JSON-dokumentum olvasását egyetlen pufferből vagy streamből. Alapértelmezés szerint az olvasó kivételt jelez, ha észleli az első legfelső szintű dokumentumot záró nem üres karaktereket. Ezt a viselkedést a AllowMultipleValues jelölővel módosíthatja.
További információ: Több JSON-dokumentum olvasása.
Tartományok
A nagy teljesítményű kódban a spanok gyakran a sztringek szükségtelen kiosztásának elkerülésére szolgálnak. Span<T> és ReadOnlySpan<T> továbbra is forradalmasítja a kód .NET való írását, és minden kiadásban egyre több olyan metódust adnak hozzá, amelyek a spanokon működnek. .NET 9 a következő, span-nel kapcsolatos frissítéseket tartalmazza:
- Fájlsegítők
-
params ReadOnlySpan<T>Túlterhelés - Végigiterálás a ReadOnlySpan<char>.Split() szegmensein
Fájlsegítők
Az File osztály mostantól új segítőkkel rendelkezik, amelyek lehetővé teszik ReadOnlySpan<char>/ReadOnlySpan<byte> és ReadOnlyMemory<char>/ReadOnlyMemory<byte> fájlokba történő egyszerű és közvetlen írását.
Az alábbi kód hatékonyan ír egy ReadOnlySpan<char>-ot a fájlba.
ReadOnlySpan<char> text = ...;
File.WriteAllText(filePath, text);
Új StartsWith<T>(ReadOnlySpan<T>, T) és EndsWith<T>(ReadOnlySpan<T>, T) bővítménymetszeti módszereket is hozzáadtak a spanokhoz, így könnyen ellenőrizhető, hogy egy ReadOnlySpan<T> kezdő vagy végződő elem egy adott T értékkel végződik-e.
Az alábbi kód ezeket az új kényelmi API-kat használja.
ReadOnlySpan<char> text = "some arbitrary text";
return text.StartsWith('"') && text.EndsWith('"'); // false
params ReadOnlySpan<T> Túlterhelés
A C# mindig is támogatta a tömbparaméterek jelölését params. Ez a kulcsszó egyszerűsített hívási szintaxist tesz lehetővé. Például a metódus második paramétere String.Join(String, String[])-vel van megjelölve params. Ezt a túlterhelést tömbökkel hívhatja meg, vagy az értékek egyenkénti átadásával:
string result = string.Join(", ", new string[3] { "a", "b", "c" });
string result = string.Join(", ", "a", "b", "c");
A 9. .NET előtt, amikor egyenként adja át az értékeket, a C#-fordító az első hívással megegyező kódot bocsát ki úgy, hogy implicit tömböt hoz létre a három argumentum köré.
A C# 13-tól kezdve bármely olyan argumentummal használhatja params , amely gyűjteménykifejezéssel hozható létre, beleértve a spanokat (Span<T> és ReadOnlySpan<T>). Ez hasznos a használhatóság és a teljesítmény szempontjából. A C#-fordító tárolhatja az argumentumokat a veremen, körbefuttathatja őket, és továbbíthatja azt a metódusnak, így elkerülheti az egyébként eredményezett implicit tömbfoglalást.
.NET 9 több mint 60 metódust tartalmaz egy params ReadOnlySpan<T> paraméterrel. Néhány teljesen új túlterhelés, és néhány meglévő metódus, amely már igénybe vett egy ReadOnlySpan<T> , de most már megjelölt paraméterrel params. A nettó hatás az, ha .NET 9-re frissít, és újrafordítja a kódot, a kód módosítása nélkül is teljesítménybeli javulást fog látni. Ennek az az oka, hogy a fordító inkább a span-alapú túlterhelésekhez kötődik, mint a tömbalapú túlterhelésekhez.
Most például String.Join a következő túlterhelést tartalmazza, amely megvalósítja az új mintát: String.Join(String, ReadOnlySpan<String>)
Most olyan hívás történik, mint a(z) string.Join(", ", "a", "b", "c") tömb létrehozása nélkül, hogy átadja a "a", "b", és "c" argumentumokat.
A ReadOnlySpan<char>.Split() által létrehozott szegmensek bejárása
string.Split egy kényelmes módszer a szövegek gyors felosztására egy vagy több megadott elválasztóval. A teljesítményre összpontosító kód esetében azonban a foglalási string.Split profil tiltó lehet, mivel minden elemzett összetevőhöz kioszt egy sztringet, és egy-egy string[] karakterláncot az összes tárolásához. Nem működik a spanokkal sem, ezért ha van egy ReadOnlySpan<char>, akkor egy újabb sztringet kell lefoglalnia, amikor sztringgé alakítja, hogy meghívhassa string.Split azt.
A .NET 8-ban egy Split és SplitAny metódusokat tartalmazó halmaz került bevezetésre ReadOnlySpan<char> számára. Ahelyett, hogy újat string[]ad vissza, ezek a metódusok egy célhelyet Span<Range> fogadnak el, amelybe az egyes összetevőkre vonatkozó határoló indexek meg vannak írva. Így a művelet teljesen kiosztásmentessé válik. Ezeket a módszereket akkor érdemes használni, ha a tartományok száma ismert és kicsi is.
.NET 9-ben új túlterheléseket adtak hozzá a Split és SplitAny számára, hogy lehetővé tegyék a ReadOnlySpan<T> elemzését, amely a priori ismeretlen számú szegmensből áll. Az új metódusok lehetővé teszik az egyes szegmensek számbavételét, amely hasonlóan jelenik meg, mint egy Range olyan, amellyel az eredeti szakaszra szeletelhető.
public static bool ListContainsItem(ReadOnlySpan<char> span, string item)
{
foreach (Range segment in span.Split(','))
{
if (span[segment].SequenceEquals(item))
{
return true;
}
}
return false;
}
Rendszer.Formátumok
A TarEntry objektumért felelős adat pozíciója vagy eltolása a befogadó adatfolyamban mostantól nyilvános tulajdonság. TarEntry.DataOffset visszaadja a bejegyzés archív adatfolyamában lévő pozíciót, ahol a bejegyzés első adat bájtja található. A bejegyzés adatai egy olyan alstreambe kerülnek, amelyen keresztül TarEntry.DataStreamhozzáférhet, amely elrejti az adatok valós pozícióját az archív adatfolyamhoz képest. Ez a legtöbb felhasználó számára elegendő, de ha nagyobb rugalmasságra van szüksége, és meg szeretné ismerni az adatok valódi kiindulási pozícióját az archív streamben, az új TarEntry.DataOffset API megkönnyíti az olyan funkciók támogatását, mint például az egyidejű hozzáférés nagyon nagy TAR-fájlokkal.
// Create stream for tar ball data in Azure Blob Storage.
BlobClient blobClient = new(connectionString, blobContainerName, blobName);
Stream blobClientStream = await blobClient.OpenReadAsync(options, cancellationToken);
// Create TarReader for the stream and get a TarEntry.
TarReader tarReader = new(blobClientStream);
System.Formats.Tar.TarEntry? tarEntry = await tarReader.GetNextEntryAsync();
if (tarEntry is null)
return;
// Get position of TarEntry data in blob stream.
long entryOffsetInBlobStream = tarEntry.DataOffset;
long entryLength = tarEntry.Length;
// Create a separate stream.
Stream newBlobClientStream = await blobClient.OpenReadAsync(options, cancellationToken);
newBlobClientStream.Seek(entryOffsetInBlobStream, SeekOrigin.Begin);
// Read tar ball content from separate BlobClient stream.
byte[] bytes = new byte[entryLength];
await newBlobClientStream.ReadExactlyAsync(bytes, 0, (int)entryLength);
System.Guid
NewGuid() hoz létre egy Guid-et, amely többnyire kriptográfiailag biztonságos véletlenszerű adatokkal van tele, az RFC 9562 szerinti UUID 4-es verzióspecifikációnak megfelelően. Ugyanez az RFC más verziókat is definiál, beleértve a 7-es verziót is, amelyek "a széles körben implementált és jól ismert Unix Epoch időbélyegforrásból származó időrendes értékmezőt tartalmaznak". Más szóval az adatok nagy része még mindig véletlenszerű, de egy része időbélyeg alapján van fenntartva az adatok számára, ami lehetővé teszi ezeknek az értékeknek a természetes rendezési sorrendjét. A .NET 9-ben a Guid létrehozható a 7-es verzió szerint az új Guid.CreateVersion7() és Guid.CreateVersion7(DateTimeOffset) metódusok használatával. Az új Version tulajdonság használatával is lekérheti az Guid objektum verziómezőjét.
System.IO
- Tömörítés zlib-ng használatával
- ZLib és Brotli tömörítési lehetőségek
- XPS-dokumentumok XPS virtuális nyomtatóról
Tömörítés zlib-ng használatával
System.IO.Compression olyan funkciók, mint a ZipArchive, DeflateStream, GZipStreamés ZLibStream mind elsősorban a zlib könyvtáron alapulnak. A .NET 9-től kezdve ezek a funkciók inkább zlib-ng kódtárat használnak, amely konzisztensebb és hatékonyabb feldolgozást biztosít az operációs rendszerek és hardverek szélesebb skálájában.
ZLib és Brotli tömörítési lehetőségek
ZLibCompressionOptions és BrotliCompressionOptions új típusok az algoritmusspecifikus tömörítési szint és stratégia (Default, , Filtered, HuffmanOnly, RunLengthEncodingvagy Fixed) beállításához. Ezek a típusok azoknak a felhasználóknak szólnak, akik részletesebb beállításokat szeretnének, mint az egyetlen meglévő beállítás, <a System.IO.Compression.CompressionLevel>.
Az új tömörítési beállítástípusok a jövőben bővíthetők.
Az alábbi kódrészlet néhány példahasználatot mutat be:
private MemoryStream CompressStream(Stream uncompressedStream)
{
MemoryStream compressorOutput = new();
using ZLibStream compressionStream = new(
compressorOutput,
new ZLibCompressionOptions()
{
CompressionLevel = 6,
CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly
}
);
uncompressedStream.CopyTo(compressionStream);
compressionStream.Flush();
return compressorOutput;
}
XPS-dokumentumok XPS virtuális nyomtatóról
A V4 XPS virtuális nyomtatóról érkező XPS-dokumentumok korábban nem nyithatóak meg a System.IO.Packaging kódtár használatával, mert nem támogatták a .piece fájlok kezelését. Ezt a hiányosságot a 9. .NET megoldottuk.
System.Numerics
BigInteger felső korlát
BigInteger támogatja a lényegében tetszőleges hosszúságú egész számértékek ábrázolását. A gyakorlatban azonban a hosszt a mögöttes számítógép korlátai korlátozzák, például a rendelkezésre álló memória vagy egy adott kifejezés kiszámításának időtartama. Emellett léteznek olyan API-k is, amelyek nem adnak meg olyan bemeneteket, amelyek túl nagy értéket eredményeznek. Ezen korlátok miatt a 9 .NET a maximális BigInteger hosszt kényszeríti ki, ami azt jelzi, hogy legfeljebb (2^31) - 1 (körülbelül 2,14 milliárd) bitet tartalmazhat. Ez a szám közel 256 MB-os foglalást jelent, és körülbelül 646,5 millió számjegyet tartalmaz. Ez az új korlát biztosítja, hogy a közzétett API-k megfelelően viselkednek és konzisztensek legyenek, miközben továbbra is lehetővé teszik a legtöbb használati forgatókönyvön túlmutató számokat.
BigMul API
BigMul egy olyan művelet, amely két szám teljes szorzatát állítja elő. .NET 9 dedikált BigMul API-kat ad hozzá a int, long, uint és ulong-hez, amelyek visszatérési típusa egy fokozattal nagyobb egész számtípus, mint a paramétertípusok.
Az új API-k a következők:
-
BigMul(Int32, Int32) (visszaadja
long) -
BigMul(Int64, Int64) (visszaadja
Int128) -
BigMul(UInt32, UInt32) (visszaadja
ulong) -
BigMul(UInt64, UInt64) (visszaadja
UInt128)
Vektorkonvertálási API-k
.NET 9 dedikált bővítmény API-kat ad hozzá a Vector2, Vector3, Vector4, Quaternion és Plane közötti konvertáláshoz.
Az új API-k a következők:
- AsPlane(Vector4)
- AsQuaternion(Vector4)
- AsVector2(Vector4)
- AsVector3(Vector4)
- AsVector4(Plane)
- AsVector4(Quaternion)
- AsVector4(Vector2)
- AsVector4(Vector3)
- AsVector4Unsafe(Vector2)
- AsVector4Unsafe(Vector3)
Az azonos méretű átalakítások, mint például a Vector4, Quaternion és Plane közötti átalakítások, nulla költséggel járnak. Ugyanez mondható el a szűkítő konverziók esetén, például Vector4-ból/ből Vector2-ba/be vagy Vector3-ba/be. Az olyan átalakítások szélesítéséhez, mint a from Vector2 vagy Vector3 to Vector4, létezik a normál API, amely inicializálja az új elemeket 0-ra, és egy Unsafe utótagú API, amely nem definiálja ezeket az új elemeket, ezért nulla költséggel jár.
API-k vektor létrehozásához
A Create, Vector, Vector2, Vector3 és Vector4 API-k elérhetők, amelyek megfelelnek a System.Runtime.Intrinsics névtérben közzétett hardvervektor-típusok megfelelő API-jainak.
Az új API-kkal kapcsolatos további információkért lásd:
Ezek az API-k elsősorban a .NET SIMD-gyorsított típusainak kényelmét és általános konzisztenciáját szolgálják.
További gyorsítás
További teljesítménybeli fejlesztések történtek a System.Numerics névtér számos típusában, beleértve a BigInteger, Vector2, Vector3, Vector4, Quaternion és Plane is.
Bizonyos esetekben ez a mag API-k 2-5-szörös gyorsulásához vezetett, beleértve a Matrix4x4 szorzást, a csúcsok sorozatából történő Plane létrehozást, a Quaternion összefűzést és a Vector3 kereszttermék kiszámítását.
Az SinCos API rendelkezik folyamatos összecsukási támogatással, amely lehetővé teszi, hogy egyetlen hívás során egyszerre kiszámítson mind Sin(x) mind Cos(x), így hatékonyabbá téve a folyamatot.
Tenzorok a mesterséges intelligenciához
A tenzorok a mesterséges intelligencia (AI) alapvető adatstruktúrái. Ezek gyakran többdimenziós tömbökként is felfoghatók.
A tenzorok a következőkre használhatók:
- Olyan adatok ábrázolása és kódolása, mint a szövegütemezések (jogkivonatok), képek, videók és hanganyagok.
- A magasabb dimenziójú adatok hatékony kezelése.
- Számításokat hatékonyan alkalmazhat a magasabb dimenziójú adatokra.
- Súlyadatok és köztes számítások tárolása (neurális hálózatokban).
A .NET tensor API-k használatához telepítse a System.Numerics.Tensors NuGet-csomagot.
Új Tensor<T-típus>
Az új Tensor<T> típus kibővíti a .NET kódtárak és futtatókörnyezetek AI-képességeit. Ez a típus:
- Hatékonyan működik együtt AI könyvtárakkal, mint például az ML.NET, a TorchSharp és az ONNX Runtime, törekedve arra, hogy elkerülje a szükségtelen adatmásolásokat, amikor csak lehetséges.
- TensorPrimitives-ra építkezve hatékony matematikai műveleteket valósít meg.
- Egyszerű és hatékony adatkezelést tesz lehetővé indexelési és szeletelési műveletek biztosításával.
- Nem helyettesíti a meglévő AI- és gépi tanulási kódtárakat. Ehelyett az API-k közös készletét hivatott biztosítani a kódkettőzés és a függőségek csökkentéséhez, valamint a legújabb futtatókörnyezeti funkciók használatával jobb teljesítmény eléréséhez.
Az alábbi kódok az új Tensor<T> típushoz tartozó API-k némelyikét mutatják be.
// Create a tensor (1 x 3).
Tensor<int> t0 = Tensor.Create([1, 2, 3], [1, 3]); // [[1, 2, 3]]
// Reshape tensor (3 x 1).
Tensor<int> t1 = t0.Reshape(3, 1); // [[1], [2], [3]]
// Slice tensor (2 x 1).
Tensor<int> t2 = t1.Slice(1.., ..); // [[2], [3]]
// Broadcast tensor (3 x 1) -> (3 x 3).
// [
// [ 1, 1, 1],
// [ 2, 2, 2],
// [ 3, 3, 3]
// ]
var t3 = Tensor.Broadcast<int>(t1, [3, 3]);
// Math operations.
var t4 = Tensor.Add(t0, 1); // [[2, 3, 4]]
var t5 = Tensor.Add(t0.AsReadOnlyTensorSpan(), t0); // [[2, 4, 6]]
var t6 = Tensor.Subtract(t0, 1); // [[0, 1, 2]]
var t7 = Tensor.Subtract(t0.AsReadOnlyTensorSpan(), t0); // [[0, 0, 0]]
var t8 = Tensor.Multiply(t0, 2); // [[2, 4, 6]]
var t9 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t0); // [[1, 4, 9]]
var t10 = Tensor.Divide(t0, 2); // [[0.5, 1, 1.5]]
var t11 = Tensor.Divide(t0.AsReadOnlyTensorSpan(), t0); // [[1, 1, 1]]
Megjegyzés:
Ez az API kísérleti jellegűnek van megjelölve a .NET 9-hez.
TensorPrimitives
A System.Numerics.Tensors kódtár tartalmazza az TensorPrimitives osztályt, amely statikus metódusokat biztosít numerikus műveletek végrehajtásához az értékeken. A .NET 9-ben a TensorPrimitives által közzétett módszerek köre jelentősen kibővült, 40-ről (a .NET 8-ban) közel 200-ra nőtt túlterhelésre. A felület olyan ismert numerikus műveleteket foglal magában, mint például Math és MathF. Az általános matematikai felületeket is tartalmazza, például INumber<TSelf>az egyéni értékek feldolgozása helyett az értékek egy tartományát dolgozzák fel. Számos művelet felgyorsult a SIMD-re optimalizált implementációk révén a .NET 9-hez.
TensorPrimitives mostantól általános túlterheléseket tesz elérhetővé minden olyan típus T esetében, amely egy bizonyos interfészt implementál. (A .NET 8 verzió csak a float értékek tartományainak manipulálására tartalmazott túlterheléseket.) Az új CosineSimilarity<T>(ReadOnlySpan<T>, ReadOnlySpan<T>) túlterhelés lehetővé teszi, hogy koszinuszos hasonlóságot hajtson végre két float, double vagy Half értékeket tartalmazó vektor között, vagy bármely más típusú értékek esetén, amelyek megvalósítják a IRootFunctions<TSelf> implementálását.
Hasonlítsa össze a koszinusz hasonlósági művelet pontosságát két típusvektoron a következővel floatdouble:
ReadOnlySpan<float> vector1 = [1, 2, 3];
ReadOnlySpan<float> vector2 = [4, 5, 6];
Console.WriteLine(TensorPrimitives.CosineSimilarity(vector1, vector2));
// Prints 0.9746318
ReadOnlySpan<double> vector3 = [1, 2, 3];
ReadOnlySpan<double> vector4 = [4, 5, 6];
Console.WriteLine(TensorPrimitives.CosineSimilarity(vector3, vector4));
// Prints 0.9746318461970762
Szálkezelés
A szálkezelési API-k a tevékenységek iterálását érintő fejlesztéseket, a rangsorolásos csatornákat is tartalmazzák, amelyek az első be, első ki (FIFO) helyett az elemeket rendezhetik, és Interlocked.CompareExchange további típusok esetén is fejlesztéseket tartalmaznak.
Task.WhenEach
Számos hasznos új API-k lettek hozzáadva a Task<TResult> objektumokkal való munkához. Az új Task.WhenEach módszer lehetővé teszi, hogy a feladatokon végighaladjon azok befejezésekor egy await foreach utasítással. Többé nem kell olyan műveleteket végrehajtania, mint például egy feladatkészlet ismételt meghívása Task.WaitAny a következő befejező feladat kiválasztásához.
Az alábbi kód több HttpClient hívást indít, és az eredményeken végrehajt műveleteket, ahogy elkészülnek.
using HttpClient http = new();
Task<string> dotnet = http.GetStringAsync("http://dot.net");
Task<string> bing = http.GetStringAsync("http://www.bing.com");
Task<string> ms = http.GetStringAsync("http://microsoft.com");
await foreach (Task<string> t in Task.WhenEach(bing, dotnet, ms))
{
Console.WriteLine(t.Result);
}
Rangsorolt, kötetlen csatorna
A System.Threading.Channels névtér lehetővé teszi az első be, első ki (FIFO) csatornák létrehozását a CreateBounded és CreateUnbounded metódusok használatával. A FIFO-csatornákkal az elemeket a rendszer abban a sorrendben olvassa be a csatornából, hogy azok meg lettek írva. .NET 9-ben egy új CreateUnboundedPrioritized metódust adtak hozzá, amely úgy rendezi el az elemeket, hogy a csatornából beolvasott következő elem Comparer<T>.Default vagy egyéni IComparer<T> szerint legyen a legfontosabb.
Az alábbi példa az új módszerrel hoz létre egy csatornát, amely 1–5 sorrendben adja ki a számokat, annak ellenére, hogy más sorrendben vannak megírva a csatornára.
Channel<int> c = Channel.CreateUnboundedPrioritized<int>();
await c.Writer.WriteAsync(1);
await c.Writer.WriteAsync(5);
await c.Writer.WriteAsync(2);
await c.Writer.WriteAsync(4);
await c.Writer.WriteAsync(3);
c.Writer.Complete();
while (await c.Reader.WaitToReadAsync())
{
while (c.Reader.TryRead(out int item))
{
Console.Write($"{item} ");
}
}
// Output: 1 2 3 4 5
Interlocked.CompareExchange további típusokhoz
A .NET korábbi verzióiban Interlocked.Exchange, Interlocked.CompareExchange és túlterhelyezték a int, uint, long, ulong, nint, nuint, float, double és object objektumokkal való munkavégzéshez, valamint volt egy általános túlterhelés a bármely referenciatípussal való munka számára is. .NET 9-ben új túlterhelések vannak atomi műveletek végrehajtására a byte, sbyte, short és ushort elemekkel. Emellett az általános Interlocked.Exchange<T> és Interlocked.CompareExchange<T> túlterhelésekre vonatkozó általános korlátozás el lett távolítva, így ezek a módszerek már nem lesznek korlátozva arra, hogy csak referenciatípusokkal működjenek. Most már bármilyen primitív típussal dolgozhatnak, amely magában foglalja az összes fent említett típust, valamint bool és charbármilyen típust enum .