.NET 11.0 Preview 4: A colorful bouquet of API extensions
The fourth preview brings new methods to existing classes in the .NET base class library and a new configuration file for Entity Framework Core.
(Image: Pincasso / Shutterstock.com)
- Dr. Holger Schwichtenberg
The fourth preview of the upcoming .NET version 11.0 has been released and is available for download. In parallel, Microsoft has also released version 11811.120 of Visual Studio 2026 Insiders, which is required for developing .NET 11.0 applications. Alternatively, you can work with Visual Studio Code and the command-line compiler included in the SDK.
(Image:Â King / stock.adobe.com)
New in .NET 11.0: Dr. Holger Schwichtenberg and other experts will present the changes for developers in .NET SDK, C# 15.0, and more at the online conference betterCode() .NET 11.0 on November 17, 2026. Until the program is released, discounted blind bird tickets are available.
Much New for Process Startup
The class System.Diagnostics.Process for managing operating system processes has existed since version 1.0 of the classic .NET Framework from 2002. Processes have been started since then by creating a new instance of the class. Since .NET Framework 2.0 (2005), the static method Process.Start() has been an alternative. 21 years later, Microsoft is now adding further alternative static methods for starting processes: Process.Run() and Process.RunAsync(), as well as Process.RunAndCaptureText() and Process.RunAndCaptureTextAsync(). The latter pair returns a ProcessTextOutput object, which allows direct access to standard output (ProcessTextOutput), standard error output (StandardError), and the return value (ExitStatus.ExitCode), with significantly less code than was necessary with the old Start() method; see listing.
Aborting the child process is possible via a cancellation token. Unlike the Start() method, all new methods with “Run” in their name only return to the caller when the child process has finished. However, developers cannot process any output from the process while it is running.
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: Using the new method Process.RunAndCaptureTextAsync()
Another method added for process startup is Process.StartAndForget() for starting a process without waiting for successful startup and without direct interaction possibilities with the new process. You can only monitor the new process from the outside via the returned process ID, but you have no access to the process's return value.
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: Using the new method Process.StartAndForget()
In the ProcessStartInfo class, used with Process.Start(), there are also two new Boolean options: New are ProcessStartInfo.StartDetached for starting a detached process with its own console, which continues to run even if the starting process terminates. With ProcessStartInfo.KillOnParentExit, the child process terminates when the starting process terminates. If both options are used in combination, you get a separate console that terminates when the starting process terminates. While ProcessStartInfo.StartDetached runs on all platforms, ProcessStartInfo.KillOnParentExit currently reports that it only works on Windows, as Microsoft's source code states:
[SupportedOSPlatform("windows")]
public bool KillOnParentExit { get; set; }
In a blog post, there is already a hint that implementations for Android and Linux are in progress.
Videos by heise
For processes started with Process.Start(), there are also the new methods ReadAllText() and ReadAllTextAsync(), with which you can get the standard output and error output of a terminated process simultaneously:
process.WaitForExit();
(string output, string error) = process.ReadAllText();
Unlike the previous approach
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
the new methods do not pose a risk of deadlock.
(De)compression with Span<T>
In .NET 11.0 Preview 1, Microsoft added Zstandard compression. The classes ZstandardEncoder and ZstandardDecoder, like the BrotliEncoder and BrotliDecoder classes introduced in .NET Core 2.1, offer the ability to work with Span<byte> and ReadOnlySpan<byte> types during compression and decompression, without the complex memory allocation associated with streams. Now, Microsoft is also providing this option for the older classes ZLibEncoder, DeflateEncoder, and GZipEncoder, as well as their corresponding decoders, see 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: Compression and decompression with Span<T>
Parsing Hex Values
The floating-point type classes Half, Single, and Double can also evaluate strings with hexadecimal numbers in the Parse() and TryParse() methods. To do this, developers must specify the NumberStyles.HexFloat option:
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
}
Checking Validity for UTF8 and UTF16
The classes System.Text.Unicode.Utf8 and System.Text.Unicode.Utf16 now offer two new methods: IsValid() and IndexOfInvalidSubsequence(). This makes it easier to check the validity of a Unicode string and at least determine the first erroneous position:
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));
Reusing Utf8JsonWriter with Different Settings
In the JSON serializer provided with modern .NET, the NuGet package System.Text.Json, which also works in the classic .NET Framework, the previously existing Reset() method in the Utf8JsonWriter class now has an overload where you can specify different settings via JsonWriterOptions. This allows developers to reuse Utf8JsonWriter instances with different settings:
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
});
In the source generator within System.Text.Json, Microsoft is fixing some weaknesses.