Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel werden neue Features in den .NET-Bibliotheken für .NET 11 beschrieben. Es wurde zuletzt für Preview 7 aktualisiert.
Diagnose und Prozessausführung
Prozess-API-Erweiterung
Process verfügt über eine Vielzahl neuer APIs, die häufige Anwendungsfälle abdecken, für die Sie zuvor OutputDataReceived/ErrorDataReceived-Ereignisse manuell verdrahten oder P/Invoke verwenden mussten.
Hilfsprogramme zum Ausführen und Erfassen
Mit neuen One-Shot-APIs können Sie einen Prozess starten und das Ergebnis ohne manuelle Einrichtung erhalten:
// One-shot capture: stdout and stderr together, plus exit code.
ProcessTextOutput result = await Process.RunAndCaptureTextAsync(
"git", ["status", "--porcelain"]);
Console.WriteLine(result.StandardOutput);
Console.WriteLine($"Exit code: {result.ExitStatus.ExitCode}");
Der vollständige Satz von Helfern umfasst:
- Process.Run und Process.RunAsync – starten Sie einen Prozess, und geben Sie ein Exit-Status-Ergebnis zurück.
- Process.RunAndCaptureText und Process.RunAndCaptureTextAsync — starten und erfassen stdout/stderr zusammen mit dem Exit-Code.
- Process.ReadAllText(Nullable<TimeSpan>), Process.ReadAllBytes(Nullable<TimeSpan>) und deren asynchrone Varianten – lesen Sie die Standardausgabe eines untergeordneten Prozesses in einem einzigen Aufruf.
- Process.ReadAllLines(Nullable<TimeSpan>) — liest synchron alle Ausgabezeilen eines untergeordneten Prozesses und gibt eine Abfolge von ProcessOutputLine Werten zurück, die stdout von stderr unterscheiden.
- Process.ReadAllLinesAsync(CancellationToken) — gibt einen Datenstrom von ProcessOutputLine Werten zurück, der stdout von stderr ohne Zeichenfolgenanalyse unterscheidet.
Starten ohne Rückmeldung
- Process.StartAndForget – startet einen untergeordneten Prozess, wenn Sie nicht darauf warten möchten; die Laufzeit trennt das Handle automatisch.
- ProcessStartInfo.StartDetached – wird von der Sitzung/Konsole des übergeordneten Prozesses getrennt, sodass der untergeordnete Prozess auch nach dem Beenden des Terminals weiterlaufen kann.
- ProcessStartInfo.KillOnParentExit (nur unter Windows) – der untergeordnete Prozess wird beendet, wenn der übergeordnete Prozess beendet wird.
SafeProcessHandle-Lebenszyklusmethoden
SafeProcessHandle erhält Lebenszyklusmethoden für fortgeschrittene Szenarien:
- SafeProcessHandle.Start(ProcessStartInfo) und eine neue SafeProcessHandle.ProcessId Eigenschaft – Prozesse starten und identifizieren, ohne Process selbst zu durchlaufen.
- SafeProcessHandle.Kill() und SafeProcessHandle.Signal(PosixSignal) — einen Prozess anhand eines Handles beenden oder signalisieren.
- SafeProcessHandle.WaitForExit() und SafeProcessHandle.WaitForExitAsync(CancellationToken) – warten, bis ein Prozess per Handle beendet wird.
Präzisere Steuerung des Hebels
-
ProcessStartInfo.InheritedHandles – gibt genau an, welche Betriebssystemfunktionen ein untergeordneter Prozess übernimmt, anstatt die Standardoption
UseShellExecute = falsezu verwenden, bei der alles oder nichts gilt. - ProcessStartInfo.StandardInputHandle, ProcessStartInfo.StandardOutputHandle und ProcessStartInfo.StandardErrorHandle – geben Sie bereits geöffnete Microsoft.Win32.SafeHandles.SafeFileHandle Werte für die Umleitung an, ohne dass das Framework neue öffnet.
Angehaltene Starts und Prozesssuche
System.Diagnostics.Process bietet eine bessere Kontrolle über das Starten und Suchen von Prozessen:
- ProcessStartInfo.StartSuspended startet einen Prozess in einem angehaltenen Zustand unter Windows und macOS. Kombinieren Sie es mit SafeProcessHandle.Resume(), damit es ausgeführt wird, sobald Sie alle Einrichtungsschritte abgeschlossen haben, z. B. das Anhängen eines Debuggers oder das Konfigurieren von Auftragsobjekten.
-
Process.TryGetProcessById(Int32, Process) gibt
falsezurück, anstatt eine Ausnahme auszulösen, wenn kein Prozess mit der angegebenen ID existiert. - <xref:Microsoft.Win32.SafeHandles.SafeProcessHandle.Open(System.Int32)> und <xref:Microsoft.Win32.SafeHandles.SafeProcessHandle.TryOpen(System.Int32,Microsoft.Win32.SafeHandles.SafeProcessHandle@)> öffnen ein Handle für einen vorhandenen Prozess anhand seiner ID.
var startInfo = new ProcessStartInfo("worker.exe") { StartSuspended = true };
using var process = Process.Start(startInfo)!;
// Attach diagnostics or configure job objects while the process is suspended,
// then call Resume to start execution once your setup is complete.
AttachProfiler(process);
process.SafeHandle.Resume();
Konsolen-FORCE_COLOR-Unterstützung
.NET Konsolenausgabe berücksichtigt nun den Standard FORCE_COLOR neben der vorhandenen NO_COLOR-Unterstützung. Wenn FORCE_COLOR gesetzt ist, unterdrückt Console.IsOutputRedirected ANSI-Escape-Codes nicht mehr. Dies ist nützlich, wenn Sie die Ausgabe von dotnet run durch tee weiterleiten, an einen CI-Protokoll-Viewer oder durch less -R:
FORCE_COLOR=1 dotnet run | tee build.log
Arbeitsspeicher-Streamadapter
Vier neue Stream-Typen kapseln gängige In-Memory-Daten, sodass Sie sie an jede API übergeben können, die ein Stream erwartet, ohne sie zuvor in ein MemoryStream zu kopieren:
-
ReadOnlyMemoryStreammacht einenReadOnlyMemory<byte>als schreibgeschützten Datenstrom verfügbar. -
WritableMemoryStreammacht einen schreibbarenMemory<byte>Datenstrom mit fester Größe verfügbar. -
ReadOnlySequenceStreamstellt einenReadOnlySequence<byte>(z. B. Puffer vonSystem.IO.Pipelines) als Datenstrom bereit, ohne ihn zu einer flachen Struktur zusammenzuführen. -
StringStreamliest einenstringoderReadOnlyMemory<char>als Datenstrom mit einer angegebenen Codierung.
using System.IO;
using System.Text;
// Pass a string to an API that takes a Stream, with no intermediate byte[] needed.
using Stream config = new StringStream(yamlText, Encoding.UTF8);
var settings = ParseConfiguration(config);
// Expose an existing buffer as a read-only stream, for example as an HTTP request body.
ReadOnlyMemory<byte> payload = GetPayload();
using Stream body = new ReadOnlyMemoryStream(payload);
await httpClient.PostAsync(uri, new StreamContent(body));
ReadOnlySequenceStream ist besonders nützlich mit System.IO.Pipelines, da es direkt über die Segmente einer ReadOnlySequence<byte> arbeitet, anstatt eine zusammenhängende Kopie zuzuordnen.
Text, Serialisierung und Datenverarbeitung
- Verbesserungen bei Zeichenfolgen und Zeichen
- Verbesserungen bei der Base64-Codierung
- UTF-Validierung und Suche nach ungültigen Teilsequenzen
- Verbesserungen bei System.Text.Json
- Verbesserungen bei regulären Ausdrücken
Verbesserungen bei Zeichenfolgen und Zeichen
.NET 11 führt erhebliche Verbesserungen an Zeichenfolgen- und Zeichenbearbeitungs-APIs ein, wodurch die Arbeit mit Unicode-Zeichen und Runen erleichtert wird.
Rune-Unterstützung in String-Methoden
Die String Klasse enthält jetzt Methoden, die Parameter akzeptieren Rune , sodass Sie Zeichenfolgen mithilfe von Unicode-Skalarwerten direkt durchsuchen, ersetzen und bearbeiten können. Zu diesen neuen Methoden gehören:
-
Contains- Überprüfen Sie, ob eine Zeichenfolge eine bestimmte Rune enthält: String.Contains(Rune) und String.Contains(Rune, StringComparison). -
StartsWithundEndsWith- Überprüfen, ob eine Zeichenfolge mit einem bestimmten Runenzeichen beginnt oder endet: String.StartsWith(Rune), String.StartsWith(Rune, StringComparison), String.EndsWith(Rune) und String.EndsWith(Rune, StringComparison). -
IndexOfundLastIndexOf- Finden Sie die Position einer Rune in einer Zeichenkette: String.IndexOf(Rune), String.IndexOf(Rune, StringComparison), String.LastIndexOf(Rune) und String.LastIndexOf(Rune, StringComparison). -
Replace- Ersetzen Sie Vorkommen einer Rune durch eine andere: String.Replace(Rune, Rune). -
Split- Teilen Sie eine Zeichenfolge mit einer Rune als Trennzeichen: String.Split(Rune, StringSplitOptions) und String.Split(Rune, Int32, StringSplitOptions). -
Trim,TrimStartundTrimEnd- Kürzen von Runen aus Zeichenfolgen: String.Trim(Rune), , String.TrimStart(Rune)und String.TrimEnd(Rune).
Viele dieser Methoden enthalten Überladungen, die einen Parameter vom Typ StringComparison für kulturbewusste Vergleiche entgegennehmen.
Char.Equals mit StringComparison
Die Char Struktur enthält jetzt eine Char.Equals(Char, StringComparison) Methode, die einen StringComparison Parameter akzeptiert, wodurch Sie Zeichen kulturabhängig oder ordinal vergleichen können.
Runenunterstützung in TextInfo
Die TextInfo-Klasse bietet jetzt TextInfo.ToLower(Rune)- und TextInfo.ToUpper(Rune)-Methoden, die Rune-Parameter annehmen, mit denen Sie Fallkonvertierungen für einzelne Unicode-Skalarwerte ausführen können.
StringBuilder.MoveChunks
Um Inhalte ohne Kopieren von Zeichen zu übertragen, verwenden Sie die neue statische StringBuilder.MoveChunks(StringBuilder) Methode, mit der alle Inhalte aus einer Quelle StringBuilder in eine neue StringBuilderverschoben werden. Nach dem Aufruf hat die Quelle StringBuilder eine Länge von 0:
var source = new StringBuilder("Hello, ");
source.Append("World!");
// MoveChunks transfers all content from source to a new StringBuilder.
// After the call, source contains no characters.
StringBuilder dest = StringBuilder.MoveChunks(source);
Console.WriteLine(dest); // Hello, World!
Console.WriteLine(source.Length); // 0
Verbesserungen bei der Base64-Codierung
.NET 11 fügt dem vorhandenen Base64-Typ neue APIs und Überladungen hinzu und bietet umfassende Unterstützung für base64-Codierung und -Decodierung. Diese Ergänzungen bieten im Vergleich zu bestehenden Methoden eine verbesserte Leistung und Flexibilität.
Neue Base64-APIs
Die neuen APIs unterstützen Codierungs- und Decodierungsvorgänge mit verschiedenen Eingabe- und Ausgabeformaten:
- Codieren für Zeichen: EncodeToChars und EncodeToString
- Codierung in UTF-8: EncodeToUtf8
- Decodieren von Zeichen: DecodeFromChars
- Decodieren von UTF-8: DecodeFromUtf8
Diese Methoden bieten sowohl hochgradige Komfortmethoden (die Arrays oder Zeichenfolgen zuordnen und zurückgeben) als auch niedrigstufige span-basierte Methoden (für Nullzuordnungsszenarien).
UTF-Validierung und Suche nach ungültigen Teilsequenzen
System.Text.Unicode verfügt über zwei neue ergänzende Features.
Utf16.IsValid(ReadOnlySpan<Char>) gibt an, ob eine UTF-16-Sequenz wohlgeformt ist, ohne sie zweimal zu scannen, und Utf8.IndexOfInvalidSubsequence(ReadOnlySpan<Byte>) / Utf16.IndexOfInvalidSubsequence(ReadOnlySpan<Char>) geben die Position der ersten fehlerhaften Codeeinheitensequenz zurück (oder -1 bei gültiger Eingabe). Mit diesen Methoden können Parser, Validatoren und Serialisierer präzise Fehler anstelle von generischen Codierungsfehlermeldungen melden.
ReadOnlySpan<byte> bytes = [0xC3, 0x28]; // invalid UTF-8
int badIndex = Utf8.IndexOfInvalidSubsequence(bytes); // 0
ReadOnlySpan<char> chars = "valid \uD83D\uDC4D end"; // valid UTF-16 (👍 emoji)
bool ok = Utf16.IsValid(chars); // true
Verbesserungen bei System.Text.Json
Abrufen von generischen Typinformationen
Ein häufiges Muster beim Arbeiten mit System.Text.Json Typmetadaten ist das Abrufen eines JsonTypeInfo<T> von JsonSerializerOptions.
Bisher musste man die nicht-generische Methode GetTypeInfo(Type) manuell herunterstufen.
Die neuen generischen Methoden JsonSerializerOptions.GetTypeInfo<T>() und JsonSerializerOptions.TryGetTypeInfo<T>(JsonTypeInfo<T>) geben direkt stark typisierte Metadaten zurück, wodurch die Herunterstufung entfällt.
JsonSerializerOptions options = new(JsonSerializerDefaults.Web);
options.MakeReadOnly();
// Before: manual downcast required
JsonTypeInfo<MyRecord> info1 = (JsonTypeInfo<MyRecord>)options.GetTypeInfo(typeof(MyRecord));
// After: generic method returns the right type directly
JsonTypeInfo<MyRecord> info2 = options.GetTypeInfo<MyRecord>();
// TryGetTypeInfo variant for cases where the type may not be registered
if (options.TryGetTypeInfo<MyRecord>(out JsonTypeInfo<MyRecord>? typeInfo))
{
// Use typeInfo
_ = typeInfo;
}
Dies ist besonders hilfreich beim Arbeiten mit Quellgenerierungs-, NativeAOT- und polymorphen Serialisierungsszenarien, in denen der Typmetadatenzugriff üblich ist.
Standardwerte benennen und ignorieren
Die jetzt verfügbaren System.Text.Json Optionen zum Benennen und Ignorieren umfassen:
- JsonNamingPolicy.PascalCase: Eine neue eingebaute Benennungsrichtlinie, die Eigenschaftsnamen in PascalCase konvertiert. Es ergänzt die vorhandenen camelCase-, snake_case- und kebab-case-Richtlinien.
- Benennungsrichtlinie pro Mitglied: Mit dem neuen System.Text.Json.Serialization.JsonNamingPolicyAttribute Attribut können Sie die Benennungsrichtlinie für einzelne Eigenschaften oder Felder außer Kraft setzen, sodass Sie ein differenziertes Steuerelement ohne einen benutzerdefinierten Konverter erhalten.
- Ignorierbedingungen auf Typebene: Durch die Verwendung von System.Text.Json.Serialization.JsonIgnoreAttribute auf Klassen- oder Strukturebene wird das Standard-Ignorierverhalten für alle Mitglieder festgelegt, sodass Sie das Attribut nicht mehr bei jeder nullfähigen Eigenschaft wiederholen müssen.
// Type-level JsonIgnore: all members use WhenWritingNull by default
// Per-member JsonNamingPolicy: EventName uses camelCase even though the
// serializer options use PascalCase
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.PascalCase
};
var data = new EventData { EventName = "Launch", Notes = null };
string json = JsonSerializer.Serialize(data, options);
Console.WriteLine(json);
// {"eventName":"Launch"} -- Notes omitted (null), EventName camel-cased
Unterstützung für diskriminierte Vereinigungen in F#
Der Serializer unterstützt nun standardmäßig diskriminierte Vereinigungen in F#. Anwendungen, die Typen zwischen F#-Produzenten und C#-Konsumenten gemeinsam verwenden, benötigen für die häufigsten Formen keinen benutzerdefinierten Konverter mehr:
type Shape =
| Circle of radius: float
| Square of side: float
let json = System.Text.Json.JsonSerializer.Serialize(Circle 1.5)
// {"$type":"Circle","radius":1.5}
Utf8JsonWriter.Reset mit Optionen
Reset akzeptiert nun einen JsonWriterOptions Parameter, sodass Writer-Instanzen mit verschiedenen Optionen neu gruppiert werden können, ohne einen neuen Writer zuzuweisen:
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
writer.WriteStartObject();
writer.WriteString("name", "example");
writer.WriteEndObject();
writer.Flush();
// Reset with different options for next use — no new allocation needed
stream.SetLength(0);
writer.Reset(stream, new JsonWriterOptions { Indented = false });
Verbesserungen für SerializeAsyncEnumerable
JsonSerializer.SerializeAsyncEnumerable bietet zwei neue Funktionen:
-
PipeWriterZiel: Bei pipelinebasierten E/A-Szenarien akzeptieren neue Überladungen ein PipeWriter Als Ausgabeziel, sodass das JSON-Streaming einfach direkt integriert werden kann. -
topLevelValuesParameter: Legen Sie für die NDJSON-Ausgabe diesen Parameter so fest, dasstruejedes Element als JSON-Wert auf oberster Ebene geschrieben wird, der durch Newlines getrennt ist, anstatt alle Elemente in einem JSON-Array umzuschließen. Sowohl dieStream- als auch diePipeWriter-Überladungen unterstützen den Parameter.
static async IAsyncEnumerable<int> GenerateNumbers()
{
for (int i = 0; i < 5; i++)
{
yield return i;
await Task.Yield();
}
}
var pipe = new Pipe();
// Write a JSON array: [0,1,2,3,4]
await JsonSerializer.SerializeAsyncEnumerable(
pipe.Writer,
GenerateNumbers());
// Write NDJSON (one value per line): 0\n1\n2\n3\n4\n
await JsonSerializer.SerializeAsyncEnumerable(
pipe.Writer,
GenerateNumbers(),
topLevelValues: true);
Serialisierung des C#-Union-Typs
System.Text.Json kann jetzt C#-Union-Typen serialisieren und deserialisieren. Der Serialisierer erkennt eine Union über den neuen JsonTypeInfoKind.Union-Vertragstyp, liest und schreibt den aktiven Fall und unterstützt sowohl den reflexionsbasierten Serialisierer als auch den Quellgenerator. Wenn Sie eine Union serialisieren, schreibt System.Text.Json den Wert der jeweils aktiven Variante, sodass sich eine Union aus int und string verlustfrei serialisieren und deserialisieren lässt:
{
"id": 1,
"payload": "hello"
}
{
"id": 2,
"payload": 42
}
Mit den neuen JsonUnionAttribute und JsonUnionCaseInfo APIs können Sie zusammen mit den Typklassifizierer-APIs (JsonTypeClassifier und JsonSerializerOptions.TypeClassifiers) anpassen, wie Fälle ermittelt und benannt werden. Union-Typen sind eine Vorschaufunktion der C#-Sprache. Weitere Informationen finden Sie unter Neuigkeiten in C# 15.
Polymorphismus-Inferenz in geschlossenen Hierarchien
JsonSerializerOptions fügt JsonSerializerOptions.InferClosedTypePolymorphism hinzu, damit der Serialisierer polymorphe Metadaten für geschlossene C#-Hierarchien ableiten kann, ohne dass explizite JsonDerivedTypeAttribute Anmerkungen für jeden Basistyp erforderlich sind. Explizite Registrierungen haben weiterhin Vorrang.
Verbesserungen von regulären Ausdrücken
AnyNewLine-Option
Ein neues RegexOptions.AnyNewLine-Flag bewirkt, dass ^, $ und . den vollständigen Satz von Unicode-Zeilenumbruchzeichen als Zeilenendzeichen behandeln – nicht nur \n. Dies hilft beim Analysieren von Text, der Windows (\r\n), Unix (\n) und Unicode-spezifische Zeilenenden (\u0085, \u2028, \u2029) kombiniert.
string text = "line1\r\nline2\u0085line3\u2028line4";
// RegexOptions.AnyNewLine makes ^, $, and . treat all Unicode newline
// sequences as line terminators, not just \n.
MatchCollection matches = Regex.Matches(
text,
@"^line\d$",
RegexOptions.Multiline | RegexOptions.AnyNewLine);
Console.WriteLine(matches.Count); // 4
Fehlerbehebungen an der Regex-Engine und am Quellgenerator
.NET 11 enthält mehrere Regex-Korrekturen und Codequalitätskorrekturen:
- Das Nicht-Rückverfolgungsmodul benötigt keine superlineare Zeit mehr für bestimmte geschachtelte Schleifenmuster und erzeugt korrekte Ergebnisse für Fälle, die zuvor abgespalten waren.
- Der regex-Compiler und der Quellgenerator behandeln
resumeAtordnungsgemäß, wenn eine bedingte Eigenschaft innerhalb eines Schleifentexts angezeigt wird. - Der SYSLIB1045 Codekorrekturer erstellt keine doppelten Klassennamen mehr, wenn sie auf mehrere partielle Deklarationen derselben Klasse angewendet werden.
Komprimierungs- und Archivformate
Komprimierungsverbesserungen
.NET 11 enthält mehrere Verbesserungen an Komprimierungs-APIs.
Zugriffsmodi für ZIP-Archiveinträge
Die ZipArchiveEntry Klasse unterstützt jetzt das Öffnen von Einträgen mit bestimmten Dateizugriffsmodi über neue Überladungen: ZipArchiveEntry.Open(FileAccess) und ZipArchiveEntry.OpenAsync(FileAccess, CancellationToken). Diese Überladungen akzeptieren einen FileAccess-Parameter und ermöglichen es Ihnen, ZIP-Einträge für den Lese-, Schreib- oder Lese-/Schreibzugriff zu öffnen.
Darüber hinaus gibt es eine neue CompressionMethod-Eigenschaft, die die für einen Eintrag verwendete Komprimierungsmethode über die ZipCompressionMethod-Enumeration bekannt gibt, die Werte für Stored, Deflate und Deflate64 enthält.
ZIP CRC32-Überprüfung
ZipArchive überprüft die CRC32-Prüfsumme beim Lesen von ZIP-Einträgen. Beschädigte oder abgeschnittene Archive, die zuvor ohne Fehler durchliefen, lösen jetzt einen Fehler aus InvalidDataException, wodurch Probleme mit der Datenintegrität frühzeitig erkannt werden können.
Unterstützung für ZIP-Archivkennworte
ZipArchiveEntry unterstützt jetzt kennwortgeschützte Einträge für Lese- und Schreibvorgänge. Verwenden Sie die neuen kennwortgestützten ZipArchiveEntry.Open(ReadOnlySpan<Char>)- und ZipArchiveEntry.OpenAsync(ReadOnlySpan<Char>, CancellationToken)-Überladungen, und verwenden Sie beim Erstellen verschlüsselter Einträge das Kennwort zusammen mit ZipEncryptionMethod.
Änderung des DeflateStream- und GZipStream-Verhaltens
Ab .NET 11 schreiben DeflateStream und GZipStream immer Formatkopf- und Fußzeilen in den Ausgabedatenstrom, auch wenn keine Daten geschrieben werden. Dadurch wird sichergestellt, dass die Ausgabe ein gültiger komprimierter Datenstrom gemäß den Spezifikationen "Deflate" und "GZip" ist.
Bisher haben diese Datenströme keine Ausgabe erzeugt, wenn keine Daten geschrieben wurden, was zu einem leeren Ausgabedatenstrom führt. Diese Änderung stellt die Kompatibilität mit Tools sicher, die ordnungsgemäß formatierte komprimierte Datenströme erwarten.
Weitere Informationen finden Sie unter DeflateStream und GZipStream schreiben Kopf- und Fußzeilen für leere Nutzdaten.
Span-basierte Deflate-, ZLib- und GZip-APIs
System.IO.Compression bietet nun Span<byte>/ReadOnlySpan<byte> Einstiegspunkte zum Kodieren und Dekodieren für die Formate Deflate, ZLib und GZip. Die neuen APIs für Typen wie DeflateEncoder, ZLibEncoder und GZipEncoder entsprechen in ihrer Struktur BrotliEncoder/BrotliDecoder und den Zstandard-Primitiven. Sie können Puffer komprimieren und dekomprimieren, ohne ein Stream zuzuweisen. Dies ist nützlich für Szenarien mit hohem Durchsatz, z. B. Protokollparser, Protokollversandgeräte und Middleware, die bereits auf Spans ausgeführt werden.
ReadOnlySpan<byte> source = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello"
byte[] buffer = new byte[source.Length + 32];
Span<byte> destination = buffer;
using ZLibEncoder encoder = new();
OperationStatus status = encoder.Compress(
source, destination, out int bytesConsumed, out int bytesWritten,
isFinalBlock: true);
Console.WriteLine($"Compressed {bytesConsumed} bytes into {bytesWritten} bytes. Status: {status}");
Zstandard-Komprimierung
Die Zstandard-Komprimierungs-APIs, zum Beispiel ZstandardStream und ZstandardEncoder, sind jetzt Teil des System.IO.Compression-Namespaces, neben DeflateStream, GZipStream und BrotliStream. Die API-Oberfläche ist andernfalls unverändert.
Tar-Archivformatauswahl
Neue Überladungen von CreateFromDirectory und CreateFromDirectoryAsync akzeptieren einen TarEntryFormat-Parameter, sodass Sie das Archivformat direkt steuern können.
CreateFromDirectory stellte früher immer Pax-Archive her. Die neuen Überladungen unterstützen alle vier Tar-Formate – Pax, Ustar, GNU und V7 – zur Kompatibilität mit bestimmten Tools und Umgebungen.
// Create a GNU format tar archive for Linux compatibility
TarFile.CreateFromDirectory("/source/dir", "/dest/archive.tar",
includeBaseDirectory: true, format: TarEntryFormat.Gnu);
// Create a Ustar format archive for broader compatibility
using Stream outputStream = File.OpenWrite("/dest/ustar.tar");
TarFile.CreateFromDirectory("/source/dir", outputStream,
includeBaseDirectory: false, format: TarEntryFormat.Ustar);
// Async version
CancellationToken cancellationToken = CancellationToken.None;
await TarFile.CreateFromDirectoryAsync("/source/dir", "/dest/archive.tar",
includeBaseDirectory: true, format: TarEntryFormat.Pax,
cancellationToken: cancellationToken);
TarReader kann nun auch Einträge lesen, die die GNU Sparse Format 1.0 (PAX)-Darstellung verwenden. Die frühere Darstellung von 0.1 wurde bereits unterstützt. Wenn 1.0-Unterstützung vorhanden ist, TarReader entspricht es, was moderne tar Implementierungen standardmäßig für sparse Dateien schreiben.
Sammlungen, numerische Typen und E/A auf niedriger Ebene
- Verbesserungen bei LINQ-Joins
- IEEE 754-Dezimal-Gleitkommatypen
- Partielle numerische Analyse
- Generischer komplexer<T>
- BFloat16-Unterstützung in BitConverter
- Gleitkomma-Hexformatierung und Analyse
- Verbesserungen bei numerischen Daten
- Zufällige generische Methoden
- Cross-Lane-Vektorvorgänge
- Verbesserungen bei E/A auf niedriger Ebene
- Verbesserungen für Sammlungen
Verbesserungen bei LINQ-Joins
LINQ fügt Verknüpfungsverbesserungen hinzu, einschließlich einer neuen FullJoin Operation und Tupel-Rückgabeüberladungen für die vorhandenen Join- und GroupJoin-Methoden. Diese APIs sind auf Enumerable, Queryable und AsyncEnumerable verfügbar.
LeftJoin, RightJoin und FullJoin
Da die drei äußeren Join-Operationen direkt Tupel zurückgeben, können Sie diese mithilfe von Musterabgleich und Dekonstruktion problemlos verarbeiten:
-
Enumerable.LeftJoin - Gibt alle Elemente aus der linken (äußeren) Sequenz zurück. Wenn kein übereinstimmende Element in der rechten (inneren) Sequenz vorhanden ist, ist
defaultdas innere Element (in der Regelnullfür Referenztypen). -
Enumerable.RightJoin - Gibt alle Elemente aus der rechten (inneren) Sequenz zurück. Wenn keine übereinstimmenden Elemente in der linken (äußeren) Sequenz vorhanden sind, ist
defaultdas äußere Element (in der Regelnullfür Referenztypen). -
Enumerable.FullJoin — Gibt alle Elemente aus beiden Sequenzen zurück, wobei
defaultauf der jeweiligen Seite verwendet wird, wenn keine Übereinstimmung vorliegt.
var products = new List<(int Id, string Name, string? Category)>
{
(1, "Laptop", "Electronics"),
(2, "Mouse", "Electronics"),
(3, "Orphan", null), // No matching category
};
var categories = new List<(string Name, string Description)>
{
("Electronics", "Electronic devices"),
("Furniture", "Office furniture"), // No matching product
};
// LeftJoin: all products, matched categories (null if none)
var leftJoined = products.LeftJoin(
categories,
p => p.Category,
c => c.Name);
foreach (var (product, category) in leftJoined)
Console.WriteLine($"{product.Name}: {category.Description ?? "(none)"}");
// Laptop: Electronic devices
// Mouse: Electronic devices
// Orphan: (none)
// FullJoin: all products and categories, paired where they match
var fullJoined = products.FullJoin(
categories,
p => p.Category,
c => c.Name);
foreach (var (product, category) in fullJoined)
Console.WriteLine(
$"{product.Name ?? "(none)"}: {category.Description ?? "(none)"}");
// Laptop: Electronic devices
// Mouse: Electronic devices
// Orphan: (none)
// (none): Office furniture
IEEE 754-Dezimal-Gleitkommatypen
System.Numerics fügt Decimal32, Decimal64 und Decimal128 hinzu, welche die Semantik für dezimale Gleitkommazahlen nach IEEE 754-2019 implementieren. Diese Typen unterstützen generische Mathematik, Unendlichkeitswerte und NaN-Werte und können hilfreich sein, wenn Sie IEEE-Dezimalverhalten anstelle von Decimal benötigen.
Partielle numerische Analyse
INumberBase<TSelf>.TryParsePartial fügt Überladungen für partielles Parsing hinzu, die die verarbeitete Eingabe zurückmelden. Verwenden Sie diese API, um durch Trennzeichen getrennte Formate wie CSV zu analysieren, sodass Sie keine Teilzeichenfolgen kopieren.
Generischer Complex<T>
Complex<T> ist jetzt verfügbar, sodass komplexe Arithmetik mehrere Gleitkommatypen verwenden kann, einschließlich float, Half, und BFloat16die neuen Dezimalkommatypen.
Überladungen von Join und GroupJoin, die Tupel zurückgeben
Neue Überladungen von Enumerable.Join und Enumerable.GroupJoin geben Tupel direkt zurück, ohne dass ein Ergebnisselektor erforderlich ist, und akzeptieren einen optionalen IEqualityComparer<T>-Parameter. Die Überladung mit optionalem Vergleich ist auch für die Operationen FullJoin, LeftJoin und RightJoin verfügbar.
var products = new[] { (Id: 1, Name: "Laptop"), (Id: 2, Name: "Mouse") };
var orders = new[] { (ProductId: 1, Qty: 3), (ProductId: 1, Qty: 1) };
var result = products.Join(orders, p => p.Id, o => o.ProductId);
foreach (var (product, order) in result)
Console.WriteLine($"{product.Name}: qty={order.Qty}");
// Laptop: qty=3
// Laptop: qty=1
BFloat16-Unterstützung in BitConverter
Die BitConverter Klasse enthält jetzt Methoden zum Konvertieren zwischen BFloat16 Werten und Bytearrays oder Bitdarstellungen. Zu diesen neuen Methoden gehören:
- BitConverter.GetBytes(BFloat16) - Konvertieren eines BFloat16-Werts in ein Bytearray.
- BitConverter.ToBFloat16(Byte[], Int32) und BitConverter.ToBFloat16(ReadOnlySpan<Byte>) - Konvertieren eines Bytearrays in einen BFloat16-Wert.
-
BitConverter.BFloat16ToInt16Bits(BFloat16), BitConverter.BFloat16ToUInt16Bits(BFloat16), BitConverter.Int16BitsToBFloat16(Int16), und BitConverter.UInt16BitsToBFloat16(UInt16) - Methoden für die Konvertierung zwischen BFloat16 und deren Bitdarstellung als
shortoderushort.
BFloat16 (Brain Floating Point) ist ein 16-Bit-Gleitkommaformat, das häufig in maschinellem Lernen und wissenschaftlichem Computing verwendet wird.
Gleitkomma-Hexformatierung und Analyse
double, floatund Half kann jetzt in ihrer hexadezimalen IEEE-754-Form formatiert und analysiert werden. Die Hexadezimalform bewahrt jedes Bit des zugrunde liegenden Werts, was sie zur richtigen Wahl für Golden-File-Tests, die sprachübergreifende Interoperabilität mit C/C++ printf("%a", ...) und alle Szenarien macht, in denen die Umwandlung eines double in Dezimaltext und zurück zu verlustreich ist.
double value = Math.PI;
// Format as hexadecimal IEEE-754: preserves all bits exactly
string hex = value.ToString("X"); // e.g., "0X1.921FB54442D18P+1"
double roundTripped = double.Parse(hex, NumberStyles.HexFloat);
Console.WriteLine(roundTripped == value); // True — exact round-trip
Verbesserungen bei numerischen Daten
Matrix4x4.GetDeterminant() verwendet nun eine SSE-vektorisierte Implementierung, die die Leistung um ca. 15%verbessert.
Zufällige generische Methoden
Random erhält zwei neue generische Methoden, die mit jedem numerischen Typ arbeiten, der die entsprechenden generischen mathematischen Schnittstellen implementiert:
-
Random.NextInteger — Generiert eine zufällige ganze Zahl vom Typ
T, wobeiTimplementiertIBinaryInteger<T>undIMinMaxValue<T>. Überladungen akzeptieren eine obere Grenze oder sowohl untere als auch obere Begrenzungen. -
Random.NextBinaryFloat — Generiert einen zufälligen Gleitkommawert vom Typ
T, wobeiTIBinaryFloatingPointIeee754<T>implementiert.
// Generate a random integer of any binary integer type
int i = Random.Shared.NextInteger<int>();
long l = Random.Shared.NextInteger<long>(0L, 100L);
byte b = Random.Shared.NextInteger<byte>(maxValue: (byte)10);
// Generate a random floating-point value of any IEEE-754 type
float f = Random.Shared.NextBinaryFloat<float>();
double d = Random.Shared.NextBinaryFloat<double>();
Half h = Random.Shared.NextBinaryFloat<Half>();
Console.WriteLine($"int={i}, long={l}, byte={b}");
Console.WriteLine($"float={f}, double={d}, Half={h}");
Cross-Lane-Vektorvorgänge
Vector64<T>, Vector128<T>, Vector256<T>, Vector512<T> und Vector<T> erhalten eine Reihe von Methoden zum Erstellen und Zusammensetzen von Lanes, die zuvor manuell geschriebene Shuffles erforderten. Die neuen Methoden lassen sich in einige Kategorien einteilen:
-
Musterkonstruktion:
CreateGeometricSequence,CreateAlternatingSequenceundCreateHarmonicSequenceerstellen Sie einen Vektor aus einem Anfangswert und einer Regel. -
Verschachteln und entflechten:
Zip,ZipLower/ZipUpper,Unzip,UnzipEven/UnzipOdd. -
Neu anordnen: Die
ConcatFamilie (ConcatLowerLower,ConcatLowerUpper,ConcatUpperLower,ConcatUpperUpper) undReverse.
using System.Runtime.Intrinsics;
// {1, 2, 4, 8} — each lane is the previous lane times two
Vector128<int> powers = Vector128.CreateGeometricSequence(1, 2);
// Interleave two vectors lane-by-lane
(Vector128<int> lower, Vector128<int> upper) =
Vector128.Zip(Vector128.Create(1), Vector128.Create(2));
Diese Bausteine sind nützlich für die Bildverarbeitung, die digitale Audiosignalverarbeitung (Digital Signal Processing, DSP) und andere SIMD-intensive Workloads, die eine differenzierte Kontrolle über das Vektorelementlayout benötigen, ohne auf plattformspezifische systeminterne Elemente zurückgreifen zu müssen.
Verbesserungen bei Low-Level-I/O
SafeFileHandle Pipe-Unterstützung
SafeFileHandle gewinnt zwei neue Mitglieder:
- SafeFileHandle.Type -Eigenschaft: Gibt an, ob ein Handle eine Datei, eine Pipe, ein Socket, ein Verzeichnis oder ein anderes Betriebssystemobjekt darstellt, ohne dass plattformspezifischer Code erforderlich ist.
- SafeFileHandle.CreateAnonymousPipe(SafeFileHandle, SafeFileHandle, Boolean, Boolean) -Methode: Erstellt ein Paar verbundener anonymer Pipehandles mit unabhängigem asynchronem Verhalten für jedes Ende.
SafeFileHandle.CreateAnonymousPipe(
out SafeFileHandle readEnd,
out SafeFileHandle writeEnd,
asyncRead: true,
asyncWrite: false);
using (readEnd)
using (writeEnd)
{
// SafeFileHandle.Type reports the kind of OS object the handle refers to
Console.WriteLine(readEnd.Type); // Pipe
Console.WriteLine(writeEnd.Type); // Pipe
}
RandomAccess Pipe-Unterstützung
RandomAccess.Read und RandomAccess.Write funktionieren nun nicht nur mit regulären Datei-Handles, sondern auch mit nicht suchbaren Handles, wie beispielsweise Pipes.
Unter Windows verwendet Process nun Overlapped I/O für umgeleitete stdout/stderr-Ausgaben, wodurch die Blockierung des Thread-Pools in prozessintensiven Anwendungen verringert wird.
Verbesserungen bei Sammlungen
BitArray.PopCount
Die BitArray Klasse, die jetzt eine BitArray.PopCount() Methode enthält, gibt die Anzahl der Bits zurück, die im Array auf true gesetzt sind. Dies bietet eine effiziente Möglichkeit zum Zählen der gesetzten Bits ohne die manuelle Durchquerung des Arrays.
IReadOnlySet-Unterstützung in JSON-Serialisierung
Die JsonMetadataServices Klasse enthält jetzt eine JsonMetadataServices.CreateIReadOnlySetInfo Methode, die JSON-Serialisierungsunterstützung für IReadOnlySet<T> Sammlungen aktiviert.
EqualityComparer<T>.Create
Verwenden Sie die neue EqualityComparer<T>.Create-Methode, um aus einer Schlüsselselektorfunktion einen Gleichheitskomparator zu erstellen. Sie können für den Schlüsseltyp selbst ein optionales IEqualityComparer<T> übergeben:
// Create an equality comparer based on a key selector
var byName = EqualityComparer<(string Name, int Age)>.Create(p => p.Name);
var people = new HashSet<(string Name, int Age)>(byName)
{
("Alice", 30),
("Bob", 25),
("Alice", 40), // Duplicate by name — not added
};
Console.WriteLine(people.Count); // 2
Erweiterungen und Entwicklerplattform
- Grundgerüst für diskriminierte Vereinigungen
- MetadataLoadContext-Ergänzungen
- Spiegelungsverbesserungen
- Assembly.Location-Außerkraftsetzung für AssemblyLoadContext
- Weniger Konflikte in System.IO.Pipelines
- URI-Datenschemakonstante
- Verbesserungen des StringSyntax-Attributs
Grundgerüst für Unterscheidungs-Unions
Hinweis
Dies ist ein Vorschaufeature in .NET 11.
.NET 11 führt System.Runtime.CompilerServices.UnionAttribute und System.Runtime.CompilerServices.IUnion in System.Runtime.CompilerServices ein. Diese Typen sind das Laufzeitgegenstück des C#-Entwurfs für Unterscheidungs-Unions. Sie sind noch nicht direkt für Benutzer sichtbar – der C#-Compiler und die Quellgeneratoren sind die vorgesehenen Erzeuger –, werden aber mit dem Framework bereitgestellt, damit Bibliotheken schon jetzt gegen diese API-Oberfläche entwickelt werden können.
Informationen zum sprachseitigen Entwurf finden Sie im C#-Unionsvorschlag.
MetadataLoadContext-Ergänzungen
MetadataLoadContext.GetLoadContext(Assembly) gibt den Ladekontext zurück, der ein bestimmtes Assembly erzeugt hat, und entspricht damit der seit Langem auf AssemblyLoadContext vorhandenen API. Dadurch wird eine Lücke bei Tools geschlossen, die Assemblys in einem isolierten MetadataLoadContext reflektieren und von einer Assembly-Referenz zu dem Kontext zurückverfolgen müssen, zu dem sie gehört:
using System.Reflection;
using System.Reflection.Metadata;
string[] paths = [typeof(object).Assembly.Location];
using var mlc = new MetadataLoadContext(new PathAssemblyResolver(paths));
Assembly asm = mlc.LoadFromAssemblyPath(typeof(object).Assembly.Location);
MetadataLoadContext owner = MetadataLoadContext.GetLoadContext(asm)!;
Console.WriteLine(ReferenceEquals(owner, mlc)); // true
URI-Datenschema-Konstante
Es wurde eine neue Uri.UriSchemeData Konstante hinzugefügt, die das data: URI-Schema darstellt. Diese Konstante stellt eine standardisierte Methode zum Verweisen auf Daten-URIs bereit.
Spiegelungsverbesserungen
Type.GetNullableUnderlyingType
System.Type erhält eine neue virtuelle Methode, Type.GetNullableUnderlyingType()die den zugrunde liegenden Werttyp für einen Nullable<T> Typ oder null für einen nicht nullablen Typ zurückgibt. Implementierungen sind verfügbar für Type, TypeBuilder, EnumBuilder, GenericTypeParameterBuilder und TypeDelegator:
Type nullableIntType = typeof(int?);
Type? underlying = nullableIntType.GetNullableUnderlyingType();
Console.WriteLine(underlying); // System.Int32
Type nonNullable = typeof(int);
Console.WriteLine(nonNullable.GetNullableUnderlyingType() is null); // True
ConstructorInfo.GetGenericArguments
ConstructorInfo.GetGenericArguments() verfügt nun über eine Überschreibung, die eine einheitliche Möglichkeit bietet, generische Typargumente für Konstruktordefinitionen abzurufen, und damit dem Verhalten entspricht, das bereits für andere MethodBase-Unterklassen verfügbar ist.
Assembly.Location-Außerkraftsetzung für AssemblyLoadContext
AssemblyLoadContext.SetAssemblyLocationOverride(Func<Assembly,String,String>) ist ein nur einmal festlegbarer Callback, der den von Assembly.Location gemeldeten Standort überschreiben kann. Diese API hilft Hostanwendungen, die Assemblies in temporären Pfaden oder virtualisierten Speicherorten ablegen, dem Code für Diagnose und Ressourcenladen stabile, aussagekräftige Speicherorte zu melden.
Reduzierte Konflikte in System.IO.Pipelines
System.IO.Pipelines reduziert Lese- und Schreibsperrkonflikte bei hohen „Auffächern nach innen“-Workloads. Interne Änderungen umfassen einen niedrigeren Sperrbereich, die Planung lokaler Warteschlangenfortsetzungen sowie Poolaktualisierungen, die die Cachelokalität verbessern.
Verbesserungen des StringSyntax-Attributs
Die StringSyntaxAttribute Klasse enthält jetzt Konstanten für allgemeine Programmiersprachen:
- CSharp - Gibt die C#-Syntax an.
- FSharp - Gibt die F#-Syntax an.
- VisualBasic – Gibt Visual Basic Syntax an.
Diese Konstanten können mit dem StringSyntax Attribut verwendet werden, um eine bessere Toolunterstützung für Zeichenfolgenliterale bereitzustellen, die Code in diesen Sprachen enthalten.
Zwischenspeichern, Konfiguration und Protokollierung
- Konfigurationsbindung
- MemoryCache OpenTelemetry-Metriken
- Verbesserungen bei der Überprüfung des Options-Generators
- Asynchrone ChangeToken.OnChange
- HybridCache-Fabriken mit Optionsunterstützung
- Asynchrone Überprüfung mit DataAnnotations
- Konfiguration der Aktivitätsablaufverfolgung
- Generische methoden für die quellgenerierte Protokollierung
- Verbesserte Zuverlässigkeit für die Erkennung von Konfigurationsdateiänderungen
Konfigurationsbindung
Microsoft.Extensions.Configuration fügt Microsoft.Extensions.Configuration.ConfigurationIgnoreAttribute hinzu, sodass Modelle einzelne Eigenschaften deklarativ von der Bindung ausnehmen können, ohne sich auf BindNonPublicProperties-Schalter oder benutzerdefinierte Konverter zu verlassen:
public sealed class AppOptions
{
public string Endpoint { get; set; } = "";
[ConfigurationIgnore]
public string ComputedKey => Endpoint + ":default";
}
ConfigurationBinder bindet nun auch ein leeres Array an einen Konstruktorparameter, anstatt es auszuwerfen.
PhysicalFilesWatcher löst keinen Fehler mehr aus, wenn das Stammverzeichnis noch nicht vorhanden ist, und InMemoryDirectoryInfo löst .. und andere relative Segmente konsistent mit dem physischen Anbieter auf.
MemoryCache OpenTelemetry-Metriken
MemoryCache gibt jetzt einen integrierten Satz von OpenTelemetry (OTel)-kompatiblen Metriken ohne zusätzliches Adapterpaket aus. Zum Aktivieren setzen Sie MemoryCacheOptions.TrackStatistics auf true:
var cache = new MemoryCache(new MemoryCacheOptions
{
TrackStatistics = true
});
Der neue Microsoft.Extensions.Caching.Memory.MemoryCache Meter veröffentlicht vier feststellbare Instrumente:
-
dotnet.cache.requests(mit einemdotnet.cache.request.typeTag, derhitvonmissunterscheidet) dotnet.cache.evictionsdotnet.cache.entriesdotnet.cache.estimated_size
Übergeben Sie System.Diagnostics.Metrics.IMeterFactory an die neue MemoryCache.MemoryCache(IOptions<MemoryCacheOptions>, ILoggerFactory, IMeterFactory)-Konstruktorüberladung für instanzspezifische Metriken. Ohne einen solchen werden die Instrumente prozessweit auf einem gemeinsamen Zähler zusammengefasst.
Verbesserungen bei der Überprüfung des Options-Generators
OptionsBuilder<TOptions> erhält eine neue generische OptionsBuilder<TOptions>.Validate-Überladung, die einen Typparameter anstelle eines Factory-Delegaten verwendet. Der Typ muss IValidateOptions<TOptions> implementieren und im Dependency-Injection-Container registriert sein. Dadurch wird die Optionsvalidierung an das Standardmuster für DI angepasst:
services.AddSingleton<IValidateOptions<MyOptions>, MyOptionsValidator>();
services.AddOptions<MyOptions>()
.Bind(configuration.GetSection("MyOptions"))
.Validate<MyOptionsValidator>();
Asynchrone ChangeToken.OnChange
OnChange verfügt jetzt über Func<Task>-Überladungen, sodass Sie asynchrone Rückrufe ohne Verwendung von async void ausführen können. Die erneute Registrierung erfolgt jetzt, nachdem die zurückgegebene Aufgabe abgeschlossen wurde.
Optionenfähige HybridCache-Fabriken
GetOrCreateAsync verfügt jetzt über Überladungen, die einen veränderbaren Factorykontext bereitstellen. Sie können grundlegende Optionen wie die Gültigkeitsdauer und die Größe des lokalen Caches über den wertgenerierenden Rückruf festlegen.
Asynchrone Überprüfung mit DataAnnotations
System.ComponentModel.DataAnnotations unterstützt jetzt die asynchrone Überprüfung. Eine Gültigkeitsprüfungsregel, die E/A ausführen muss – eine Datenbanksuche oder ein Remote-API-Aufruf – kann jetzt ausgeführt werden, ohne einen Thread zu blockieren. Es gibt drei neue Möglichkeiten zum Ausdrücken einer asynchronen Regel:
- Leiten Sie von
AsyncValidationAttributeab und überschreiben Sie dessen MethodeIsValidAsync. - Implementieren Sie
IAsyncValidatableObjectam Modell. - Rufen Sie die neuen
Validator.ValidateObjectAsync, ,TryValidateObjectAsync,ValidatePropertyAsyncundValidateValueAsyncMethoden auf.
using System;
using System.ComponentModel.DataAnnotations;
public sealed class ValidVatNumberAttribute : AsyncValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext context) =>
throw new InvalidOperationException("Validate this attribute with IsValidAsync.");
protected override async Task<ValidationResult?> IsValidAsync(
object? value, ValidationContext context, CancellationToken cancellationToken)
{
var registry = context.GetRequiredService<IVatRegistry>();
return await registry.IsRegisteredAsync((string?)value, cancellationToken)
? ValidationResult.Success
: new ValidationResult("That VAT number isn't registered with the tax authority.");
}
}
public class Invoice
{
[Required]
[StringLength(14, MinimumLength = 8)]
[RegularExpression(@"^[A-Z]{2}[A-Z0-9]+$")]
[ValidVatNumber] // asynchronous
public string VatNumber { get; set; } = "";
}
var context = new ValidationContext(model, serviceProvider, items: null);
await Validator.ValidateObjectAsync(model, context, validateAllProperties: true);
Microsoft.Extensions.Options erhält übereinstimmende Unterstützung: Optionen können asynchron überprüft werden, einschließlich beim Start über die neue IAsyncStartupValidator. Dadurch kann eine App schnell fehlschlagen, wenn eine Option, die eine Netzwerküberprüfung erfordert, falsch konfiguriert ist.
Konfiguration der Aktivitätsverfolgung
Mit einer neuen API für die Ablaufverfolgungs-Konfiguration in Microsoft.Extensions.Diagnostics können Sie die Activity-Ablaufverfolgung mit Regeln aktivieren und deaktivieren, anstatt ActivityListener-Instanzen manuell einzurichten. Rufen Sie AddTracing auf und geben Sie an, welche Aktivitätsquellen, Vorgänge und Listener aktiviert oder deaktiviert werden sollen. Regeln können über die Konfiguration gesteuert werden, sodass die Ablaufverfolgung ohne erneute Bereitstellung angepasst werden kann.
builder.Services.AddTracing(tracing =>
{
tracing.EnableTracing(sourceName: "MyCompany.Orders");
tracing.DisableTracing(sourceName: "MyCompany.Orders", operationName: "HealthCheck");
});
Das gleiche Release fügt ActivitySourceFactory hinzu und gibt ActivitySource frei, die beide fabrikgesteuerte Erstellung und aktualisierbare Listener unterstützen.
Generische methoden für die quellgenerierte Protokollierung
Der Protokollierungsquellgenerator unterstützt jetzt generische Methoden, die mit LoggerMessageAttribute versehen sind, vor allem um Boxing zu vermeiden. Generische Typparameter können Standardbeschränkungen verwenden, wie z. B. class, struct, unmanaged, Schnittstellen, Basistypen und new(). Die allows ref struct negative Einschränkung wird nicht unterstützt und erzeugt SYSLIB1011.
[LoggerMessage(0, LogLevel.Trace, "Received {Code} ({Length} bytes)")]
public static partial void PacketReceived<TCode>(ILogger logger, TCode code, int length)
where TCode : struct, Enum;
Verbesserte Zuverlässigkeit für die Erkennung von Konfigurationsdateiänderungen
Der PhysicalFilesWatcher Typ, den Konfigurationsanbieter zum Erkennen von Konfigurationsdateiänderungen verwenden, umfasst mehrere Zuverlässigkeitsverbesserungen.
Es behandelt nun relative Pfade, fehlende Verzeichnisse und Dateisysteme, die die Dateibeobachtung nicht zuverlässiger unterstützen. Außerdem vermeidet es, Unterverzeichnisse zu überwachen, wenn dies nicht nötig ist.
Kryptografie
X25519 Diffie-Hellman Schlüsselaustausch
Die neue System.Security.Cryptography.X25519DiffieHellman abstrakte Klasse bietet eine saubere API für die elliptische X25519-Kurve Diffie-Hellman Algorithmus, der häufig in TLS 1.3-, SSH- und Signalprotokollimplementierungen verwendet wird. Plattformspezifische Implementierungen werden über X25519DiffieHellmanCng (Windows) und X25519DiffieHellmanOpenSsl (Linux und macOS) bereitgestellt, aber die portable GenerateKey() Factorymethode wählt automatisch die richtige:
// Generate key pairs for Alice and Bob
using X25519DiffieHellman alice = X25519DiffieHellman.GenerateKey();
using X25519DiffieHellman bob = X25519DiffieHellman.GenerateKey();
// Each party derives the shared secret using the other's public key
byte[] aliceShared = alice.DeriveRawSecretAgreement(bob);
byte[] bobShared = bob.DeriveRawSecretAgreement(alice);
// Both parties arrive at the same secret
Console.WriteLine(aliceShared.SequenceEqual(bobShared)); // True
Die Klasse unterstützt den vollständigen Schlüssellebenszyklus: Schlüsselgenerierung, PKCS#8 und SubjectPublicKeyInfo-Import/Export, PEM-Serialisierung und unformatierter Zugriff auf private/öffentliche Schlüssel.
CryptographicOperations.FixedTimeEquals-Überladung
Verwenden Sie für Geheimvergleichsszenarien, in denen der erwartete Wert ein einzelnes bekanntes Byte ist, die neue CryptographicOperations.FixedTimeEquals Überladung, die einen ReadOnlySpan<T> mit einem einzelnen byte Wert in konstanter Zeit vergleicht:
bool equal = CryptographicOperations.FixedTimeEquals(receivedSpan, 0x42);
Netzwerk- und Transportsicherheit
- TLS-Handshake-Härtung
- HTTP-Anforderungskomprimierung
- Konfigurierbare HTTP-Verbindungsräumung
- DNS-Eintragsauflösungs-APIs
- Automatisches HTTP/2-Downgrade für die Windows-Authentifizierung
- QUIC-Stream-Priorität
- Video MIME-Typkonstanten
TLS-Handshake-Härtung
Zwei System.Net.Security Elemente verbessern die Zuverlässigkeit von TLS (Transport Layer Security):
-
SslStream serverseitige Korrekturen an der Grenzprüfung beim Handshake in
TlsFrameHelperbeheben mehrere Randfälle, die sich bei nicht wohlgeformten ClientHello-Datensätzen alsIOExceptionäußern könnten. - Unter Linux werden Fehler bei der Zertifikatvalidierung jetzt dem Peer als standardmäßige TLS-Alerts übermittelt, entsprechend dem Verhalten unter Windows. Beim Verbinden von Clients wird anstelle eines Verbindungsabbruchs ein handlungsfähiger Handshakefehler angezeigt.
HTTP-Anforderungskomprimierung
System.Net.Http fügt GZipCompressedContent-, BrotliCompressedContent- und ZstandardCompressedContent-Wrapper für Anforderungstextkörper hinzu. Diese Wrapper setzen den Content-Encoding-Header und streamen komprimierten Inhalt, während die Anfrage serialisiert wird.
Konfigurierbare HTTP-Verbindungsräumung
SocketsHttpHandler bietet jetzt SocketsHttpHandler.ShouldEvictConnection für Entscheidungen hinsichtlich Entfernungen pro Verbindung. Mit diesem Rückruf können Sie Verbindungen anhand von Alter, Endpunktdetails und DNS-Änderungen ausmustern, anstatt Richtlinien zu verwenden, die nur auf einer festen Lebensdauer basieren.
DNS-Eintragsauflösungs-APIs
System.Net.Dns fügt typisierte APIs zur Datensatzauflösung hinzu, darunter Dns.ResolveSrv(String), Dns.ResolveMx(String), Dns.ResolveCName(String), Dns.ResolveTxt(String), Dns.ResolvePtr(String) und Dns.ResolveNs(String), sowie Async-Varianten. Die Ergebnisse umfassen Datensätze, Antwortcode und Metadaten zur Gültigkeitsdauer (TTL) des negativen Caches über DnsResult<T>.
Automatisches HTTP/2-Downgrade für Windows-Authentifizierung
HttpClient wird automatisch auf HTTP/1.1 herabgestuft, wenn eine Anforderung Windows-Authentifizierung (NTLM/Negotiate) über HTTP/2 erfordert. Die HTTP/2-Spezifikation verbietet die verbindungsgebundenen Authentifizierungsschemas, auf die NTLM und Kerberos angewiesen sind, sodass diese Anforderungen zuvor fehlgeschlagen sind. Wenn das Downgrade aktiviert ist, funktionieren Anwendungen, die für Umgebungen mit gemischter Authentifizierung verwendet werden , die in Unternehmensintranets üblich sind, ohne explizite HttpRequestMessage.Version Außerkraftsetzungen.
QUIC-Streampriorität
QuicStream erhält eine QuicStream.Priority Eigenschaft und eine QuicStream.DefaultPriority Konstante, die HTTP/3-Streampriorisierung (RFC 9218) verfügbar macht. Prioritätswerte reichen von 0 (höchste) bis 255 (niedrigste) mit einem Standardwert von 127:
// Increase priority for interactive streams
stream.Priority = 64; // Higher priority than the default 127
Video MIME-Typkonstanten
Eine neue System.Net.Mime.MediaTypeNames.Video Klasse stellt Zeichenfolgenkonstanten für allgemeine Videomedientypen bereit:
-
MediaTypeNames.Video.Mp4—"video/mp4" -
MediaTypeNames.Video.Mpeg—"video/mpeg" -
MediaTypeNames.Video.Ogg—"video/ogg" -
MediaTypeNames.Video.QuickTime—"video/quicktime" -
MediaTypeNames.Video.WebM—"video/webm"
Diese verbinden die vorhandenen MediaTypeNames.Application, , MediaTypeNames.Image, MediaTypeNames.Textund MediaTypeNames.Multipart Klassen.
Unsicherer API-Zugriff
.NET 11 entfernt das attribut [RequiresUnsafe] aus einem großen Satz von APIs, die Zeigerparameter verwenden. Zuvor erforderte das Aufrufen dieser Methoden aus Code, der unsafe-Blöcke verwendete, weiterhin eine <AllowUnsafeBlocks>true</AllowUnsafeBlocks>-Einstellung auf Projektebene, da das Attribut diese Anforderung unabhängig davon durchsetzte. Jetzt ist nur noch der Standard-unsafe-Block oder Methodenmodifizierer erforderlich.
Zu den betroffenen APIs gehören:
- Buffer.MemoryCopy
- ReadOnlySpan<T>.ReadOnlySpan<T>(Void*, Int32) und Span<T>.Span<T>(Void*, Int32)
-
System.Runtime.CompilerServices.Unsafe Zeigermethoden wie
AsRef,Read, ,WriteundCopy - System.Runtime.InteropServices.NativeMemory Methodik
- System.Text.Encoding Zeigerüberladungen (alle Codierungsklassen)
-
System.Numerics.Vector zeigerbasiert
LoadundStoreMethoden - Marshallingtypen für Interop in System.Runtime.InteropServices.Marshalling
Diese Änderung verringert den Aufwand in fortgeschrittenen Szenarien wie der Erstellung von Interoperabilitätscode, speicherzugeordneten Dateien und der Manipulation von Puffern auf niedriger Ebene, wenn vom aufrufenden Code ohnehin bereits ein unsafe-Kontext erforderlich ist.