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

listen Print view
Traffic sign with inscription .NET

(Image: Pincasso / Shutterstock.com)

13 min. read
By
  • Dr. Holger Schwichtenberg
Contents

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.

The Dotnet Doctor – Holger Schwichtenberg
Der Dotnet-Doktor – Holger Schwichtenberg

Dr. Holger Schwichtenberg is the technical director of the expert network www.IT-Visions.de, which supports numerous medium-sized and large companies with consulting and training services as well as software development, drawing on the expertise of 53 renowned experts. Thanks to his appearances at numerous national and international conferences, as well as more than 90 specialist books and over 1,500 specialist articles, Holger Schwichtenberg is one of the best-known experts for .NET and web technologies in Germany.

Installation of the .NET 11.0 SDK in Preview 4
Heise Conference: betterCode() .NET 11.0
betterCode() .NET 11.0

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

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.

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>

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
}

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

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.

Don't miss any news – follow us on Facebook, LinkedIn or Mastodon.

This article was originally published in German. It was translated with technical assistance and editorially reviewed before publication.