.NET 11.0 Preview 4: Ein bunter Strauß von API-Erweiterungen

vor 1 Stunde 1

Die vierte Vorschauversion der kommenden .NET-Version 11.0 ist erschienen und steht zum Download bereit. Parallel dazu hat Microsoft auch die Version 11811.120 von Visual Studio 2026 Insiders veröffentlicht, die zum Entwickeln von .NET-11.0-Anwendungen benötigt wird. Alternativ ist eine Arbeit mit Visual Studio Code und dem im SDK mitgelieferten Kommandozeilencompiler möglich.

Dr. Holger Schwichtenberg

Dr. Holger Schwichtenberg hat Fachbücher zu .NET 10.0, C# 14.0, Blazor 10.0 und Entity Framework Core 10.0 veröffentlicht. Er arbeitet als Berater und Trainer bei www.IT-Visions.de.

Installation des .NET 11.0 SDK in der Version Preview 4

betterCode() .NET 11.0

(Bild: King / stock.adobe.com)

Das ist neu in .NET 11.0: Dr. Holger Schwichtenberg und weitere Experten präsentieren am 17. November 2026 auf der Online-Konferenz betterCode() .NET 11.0 die Änderungen für Entwicklerinnen und Entwickler in .NET SDK, C# 15.0 und mehr. Bis zur Veröffentlichung des Programms sind vergünstigte Blind-Bird-Tickets verfügbar.

Die Klasse System.Diagnostics.Process zur Verwaltung von Betriebssystemprozessen gibt es seit Version 1.0 des klassischen .NET Framework aus dem Jahr 2002. Prozesse startet man seitdem, indem man eine neue Instanz der Klasse erzeugt. Seit .NET Framework 2.0 (Jahr 2005) gibt es alternativ die statische Methode Process.Start(). 21 Jahre später ergänzt Microsoft nun weitere alternative statische Methoden zum Prozessstart: Process.Run() und Process.RunAsync() sowie Process.RunAndCaptureText() und Process.RunAndCaptureTextAsync(). Das letztgenannte Pärchen liefert ein ProcessTextOutput-Objekt, mit dem man direkt auf Standardausgabe (ProcessTextOutput), Standardfehlerausgabe (StandardError) und Rückgabewert (ExitStatus.ExitCode) zugreifen kann, mit deutlich weniger Programmcode als dies bei der alten Start()-Methode notwendig ist, siehe Listing.

Ein Abbruch des Kindprozesses ist über ein Cancellation-Token möglich. Anders als bei der Start()-Methode kehren alle neuen Methoden mit „Run“ im Namen erst zum Aufrufer zurück, wenn der Kindprozess beendet ist. Entwicklerinnen und Entwickler können dabei allerdings keine Ausgaben des Prozesses verarbeiten, während er läuft.

CancellationTokenSource cts = new CancellationTokenSource(); ProcessTextOutput result = await Process.RunAndCaptureTextAsync( "robocopy.exe", [@"t:\Daten", @"t:\Daten_Backup", "/MIR", "/IS"], cts.Token); CUI.Print("Neuer Prozess mit ID #" + result.ProcessId + " ist beendet!"); CUI.Line("StandardOutput"); CUI.Print(result.StandardOutput); CUI.Line("StandardError"); CUI.PrintError(result.StandardError); CUI.Line("ExitStatus"); CUI.Print("Canceled? " + result.ExitStatus.Canceled); if (result.ExitStatus.HasValue && !result.ExitStatus.IsEmpty) PrintStatus(result.ExitStatus.ExitCode);

Listing 1: Einsatz der neuen Methode Process.RunAndCaptureTextAsync()

Eine weitere hinzugefügte Methode zum Prozessstart ist Process.StartAndForget() zum Start eines Prozesses, ohne auf den erfolgreichen Start zu warten und ohne direkte Interaktionsmöglichkeiten mit dem neuen Prozess. Man kann lediglich über die zurückgelieferte Prozess-ID den neuen Prozess von außen überwachen, hat aber keinen Zugriff auf den Rückgabewert des Prozesses.

int processId = Process.StartAndForget( "robocopy.exe", [@"t:\Daten", @"t:\Daten_Backup", "/MIR", "/IS"]); CUI.Print("Neuer Prozess mit ID #" + processId + " ist gestartet!"); var p = Process.GetProcessById(processId); while(!p.HasExited) { CUI.BusyIndicator(); Thread.Sleep(500); } CUI.Line("Neuer Prozess mit ID #" + processId + " ist beendet!"); // PrintStatus(p.ExitCode); // System.InvalidOperationException: 'Process was not started by this object, so requested information cannot be determined.'

Listing 2: Einsatz der neuen Methode Process.StartAndForget()

In der Klasse ProcessStartInfo, die bei Process.Start() verwendet wird, gibt es auch zwei neue Boolean-Optionen: Neu sind zum einen ProcessStartInfo.StartDetached zum Start eines unabhängigen Prozesses mit eigener Konsole, der weiterlebt, auch wenn der startende Prozess beendet wird. Mit ProcessStartInfo.KillOnParentExit erreicht man zum anderen, dass der Kindprozess endet, wenn der startende Prozess endet. Wenn man beide Optionen in Kombination einsetzt, erhält man eine separate Konsole, die aber endet, wenn der startende Prozess endet. Während ProcessStartInfo.StartDetached auf allen Plattformen läuft, meldet ProcessStartInfo.KillOnParentExit aktuell, dass es nur auf Windows funktioniert, denn im Quellcode bei Microsoft steht:

[SupportedOSPlatform("windows")] public bool KillOnParentExit { get; set; }

In einem Blogeintrag findet man schon den Hinweis darauf, dass Implementierungen für Android und Linux in Arbeit sind.

Für mit Process.Start() gestartete Prozesse gibt es auch die neuen Methoden ReadAllText() und ReadAllTextAsync(), mit denen man von einem beendeten Prozess gleichzeitig die Standardausgabe und die Fehlerausgabe erhalten kann:

process.WaitForExit(); (string output, string error) = process.ReadAllText();

Im Gegensatz zu dem bisherigen Ansatz

string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd();

besteht bei den neuen Methoden nicht die Gefahr eines Deadlocks.

In .NET 11.0 Preview 1 hatte Microsoft die Zstandard-Komprimierung ergänzt. Die Klassen ZstandardEncoder und ZstandardDecoder bieten dabei genauso wie die bereits in .NET Core 2.1 eingeführten Klassen BrotliEncoder und BrotliDecoder die Möglichkeit, beim Komprimieren und Dekomprimieren mit den Typen Span<byte> und ReadOnlySpan<byte> zu arbeiten, ohne die aufwendige Speicherallokation bei Streams. Nun liefert Microsoft diese Option auch für die älteren Klassen ZLibEncoder, DeflateEncoder und GZipEncoder sowie die zugehörigen Decoder, siehe Listing.

CUI.H1($"Komprimiere Datei {BIGFILEPATH} via Span<T>"); ReadOnlySpan<byte> sourceSpan = File.ReadAllBytes(BIGFILEPATH); Console.WriteLine("Länge=" + sourceSpan.Length); long maxCompressedLength = ZLibEncoder.GetMaxCompressedLength(sourceSpan.Length); Span<byte> compressedSpan = new byte[maxCompressedLength]; // ZLibEncoder, DeflateEncoder, GZipEncoder, ZstandardEncoder oder BrotliEncoder using ZLibEncoder encoder = new(); OperationStatus status = encoder.Compress( sourceSpan, compressedSpan, out int bytesConsumed, out int bytesWritten, isFinalBlock: true); PrintStatus(compressedSpan, status); CUI.H1($"Dekomprimieren aus Span<T>"); // ZLibDecoder, DeflateDecoder, GZipDecoder, ZstandardDecoder oder BrotliDecoder using ZLibDecoder decoder = new(); byte[] decompressedSpan = new byte[sourceSpan.Length]; OperationStatus decompressStatus = decoder.Decompress( compressedSpan, decompressedSpan, out int compressedBytesConsumed, out int decompressedBytesWritten); PrintStatus(decompressedSpan, decompressStatus);

Listing 3: Komprimierung und Dekomprimierung mit Span<T>

Die Fließkommazahltypklassen Half, Single und Double können in den Methoden Parse() und TryParse() auch Zeichenketten mit Hexadezimalzahlen auswerten. Dazu müssen Entwicklerinnen und Entwickler aber die Option NumberStyles.HexFloat angeben:

static void TestDouble(double d, string doubleAsString ) { string hex = d.ToString("X"); Console.WriteLine(hex); double d1a = double.Parse(hex, NumberStyles.HexFloat); Console.WriteLine(d1a); CUI.Success(d1a == d); // True double.TryParse(hex, NumberStyles.HexFloat, null, out double d1b); Console.WriteLine(d1b); CUI.Success(d1b == d); // True }

Die Klassen System.Text.Unicode.Utf8 und System.Text.Unicode.Utf16 bieten nun zwei neue Methoden: IsValid() und IndexOfInvalidSubsequence(). Damit lässt sich nun leichter die Gültigkeit einer Unicode-Zeichenkette prüfen und zumindest die erste fehlerhafte Stelle ermitteln:

ReadOnlySpan<char> chars1 = "Gültiger Text: \uD83D\uDC4D"; Console.WriteLine(chars1); bool check1 = Utf16.IsValid(chars1); // True Console.WriteLine(check1); if (check1) CUI.Success("OK"); else CUI.Warning("Fehler bei Zeichen: " + Utf16.IndexOfInvalidSubsequence(chars1)); ReadOnlySpan<char> chars2 = "Ungültiger Text: \uD83D"; Console.WriteLine(chars2); bool check2 = Utf16.IsValid(chars2); // False if (check2) CUI.Success("OK"); else CUI.Warning("Fehler bei Zeichen: " + Utf16.IndexOfInvalidSubsequence(chars2));

Bei dem im modernen .NET mitgelieferten JSON-Serialisierer, dem NuGet-Paket System.Text.Json, das auch im klassischen .NET Framework funktioniert, bietet die schon vorher bestehende Methode Reset() in der Klasse Utf8JsonWriter nun eine Überladung, in der man via JsonWriterOptions abweichende Einstellungen festlegen kann. Entwicklerinnen und Entwickler können damit Utf8JsonWriter-Instanzen mit abweichenden Einstellungen wiederverwenden:

using var stream1 = new MemoryStream(); using var writer = new Utf8JsonWriter(stream1, new JsonWriterOptions { Indented = true }); … using var stream2 = new MemoryStream(); writer.Reset(stream2, new JsonWriterOptions { Indented = false });

Im Source Generator innerhalb von System.Text.Json behebt Microsoft einige Schwächen.

Gesamten Artikel lesen