Entwicklung & Code
.NET 11.0 Preview 3 bringt Union Types und erweitert File-based Apps
Microsoft hat .NET-Version 11.0 Preview 3 zusammen mit Visual Studio 2026 Insiders Version 11709.129 veröffentlicht. .NET 11.0 umfasst die Sprachversion 15.0 der Programmiersprache C#.
Weiterlesen nach der Anzeige

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.
Discriminated Unions
Eine Discriminated Union (oft auch Tagged Union, Algebraic Data Type oder Sum Type genannt) ist ein Datentyp, der genau einen von mehreren möglichen Typen enthalten kann, wobei jederzeit klar ist, welcher Typ gerade enthalten ist. Viele moderne Sprachen, wie F#, Rust, Swift oder TypeScript beherrschen dieses Konzept, andere wie Kotlin erlauben die Nachbildung. In C# musste man sich bisher mit dem Basistyp System.Object, Vererbung, Interfaces, Pattern Matching oder Umsetzungen auf Basis generischer Typen wie der Bibliothek OneOf behelfen.
Einen Union Type in C# 15.0 erstellen Entwicklerinnen und Entwickler mit dem neuen C#-Schlüsselwort union unter Angabe eines Namens und von einem oder mehreren Typen, beispielsweise
public union UnionName(Typ1, Typ2, Typ3);
Dabei müssen die angegebenen Typen keinerlei Gemeinsamkeiten besitzen: Weder eine Basisklasse noch eine Schnittstelle müssen sie teilen. Das Schlüsselwort null darf man nicht als Typ angeben. Nullable Values Types (z.B. int?) und Nullable Reference Types (Person?) sind aber erlaubt.
Einer Variablen des Typs UnionName kann man dann sowohl Instanzen von Typ1 und Typ2 als auch Typ3 sowie gegebenenfalls null zuweisen. Jede andere Objekttypzuweisung wird aber verhindert.
Weiterlesen nach der Anzeige
Mit dem Operator is oder Pattern Matching lässt sich abfragen, welchen konkreten Typ die Union-Typvariable enthält. Während beim Pattern Matching automatisch ein Casting auf den Zieltyp erfolgt, muss man ohne Pattern Matching die Eigenschaft Value nutzen, um an den konkreten Typ zu kommen. Allerdings liefert Value den Typ System.Object, sodass wieder ein Casting erforderlich wird.
Als Beispiel soll ein typischer Einsatzfall für Discriminated Unions dienen: Eine Operation der Geschäftslogik kann neben einem konkreten Ergebnisobjekt mit Zeichenketten, Zahlen oder Exception-Objekten verschiedene Fehlerfälle signalisieren. Beim Einsatz von switch-Ausdrücken warnt der Compiler, wenn der Block nicht alle Fälle abfragt, mit der Warnung CS8509: „The switch expression does not handle all possible values of its input type (it is not exhaustive).“
Union Types haben das Potenzial, in einigen Teilen der .NET-Anwendungsframeworks für Vereinfachungen zu sorgen, beispielsweise bei WebAPI-Operationen mit Typed Results. Aktuell ist dies noch nicht möglich, aber es steht auf der Roadmap für ASP.NET WebAPIs und ASP.NET Core SignalR sowie Blazor zur Realisierung bis Jahresende 2026.
Folgender Code zeigt einen C# 15.0 Union Type für differenzierte Rückgabetypen einer Geschäftslogikmethode:
#nullable enable
namespace NET11_Console.CS15;
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Website { get; set; }
public override string ToString()
{
return $"Person: #{ID} Name: {Name} Website: {Website}";
}
}
public union PersonOperationResult(Person?, string, int?, Exception);
class BL
{
public PersonOperationResult GetPerson(int ID)
{
try
{
if (ID <= 0) return "Ungültige Person-ID";
if (ID == 123) return new Person() { ID = ID, Name = "Dr. Holger Schwichtenberg", Website = "www.IT-Visions.de" };
if (ID == 0815) throw new ApplicationException("Anwendungsfehler");
return ID; // Person nicht gefunden
}
catch (Exception ex)
{
return ex;
}
}
}
class UnionTypeDemo
{
public void Run()
{
CUI.Demo(nameof(UnionTypeDemo));
BL BL = new();
CUI.H1("\nLade Person mit ID 123");
PersonOperationResult result1 = BL.GetPerson(123);
PrintResult(result1);
CUI.H1("\nTest mit Null-Wert");
PersonOperationResult result2 = null;
PrintResult(result2);
CUI.H1("\nLade Person mit ID 0 (Fehlerfall)");
PersonOperationResult result3 = BL.GetPerson(-1);
PrintResult(result3);
CUI.H1("\nLade Person mit ID 101 (nicht gefunden)");
PersonOperationResult result4 = BL.GetPerson(101);
PrintResult(result4);
CUI.H1("\nLade Person mit ID 0815 (Anwendungsfehler)");
PersonOperationResult result5 = BL.GetPerson(0815);
PrintResult(result5);
// Nicht erlaubt
//PersonOperationResult result = new FileInfo(@"c:\temp\Datei.xy");
}
private static void PrintResult(PersonOperationResult result)
{
if (result is int)
{
CUI.Error($"Person #{result.Value} nicht gefunden");
}
else if (result is string)
{
CUI.Error($"Fehler: {result.Value}");
}
else if (result is Exception)
{
CUI.Error($"Fehler: {(result.Value as Exception).Message}");
}
else if (result is Person)
{
CUI.Print(result.Value);
}
else if (result is null)
{
CUI.Error("Null-Wert");
}
}
private static void PrintResult_PatternMatching(PersonOperationResult result)
{
if (result is int i)
{
CUI.Error($"Person #{i} nicht gefunden");
}
else if (result is string s)
{
CUI.Error($"Fehler: {s}");
}
else if (result is Exception ex)
{
CUI.Error($"Fehler: {ex.Message}");
}
else if (result is Person p)
{
CUI.Print(p);
}
}
private static void PrintResult_Switch(PersonOperationResult result)
{
Console.WriteLine(result switch
{
Person p => $"Person: {p.Name}",
string s => $"Fehler: {s}",
Exception ex => $"Fehler: {ex.Message}",
int i => $"Person #{i} nicht gefunden",
null => "Null-Wert"
});
}
}

Das Beispiel demonstriert verschiedene Fälle beim Laden einer „Person“ anhand einer ID (Abb. 1).
Eine erste Unterstützung für Union Types gab es in Preview 2 von .NET 11.0 im März. Allerdings hatte Microsoft dieses Feature in dem Blogeintrag vergessen zu erwähnen. In den Release Notes zu .NET 11.0 Preview 2 führte der C#-Link ins Nirvana. Anfang April gab es dann einen Blogeintrag, der nachlieferte, dass Union Types in Preview 2 schon möglich waren, allerdings ohne Editor-Unterstützung. Der Editor funktioniert nun für Union Types in der Visual Studio-Version 2026 Insiders 11709.129.
Allerdings fehlt auch in Preview 3 immer noch ein Stück Code in der Basisklassenbibliothek. Neben dem Tag in der Projektdatei müssen Entwicklerinnen und Entwickler daher auch den Inhalt des folgenden Listings mit der Implementierung der Annotation [Union] via Klasse UnionAttribute in jedes Projekt aufnehmen, das Union Types nutzen will:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,
AllowMultiple = false)]
public sealed class UnionAttribute : Attribute;
public interface IUnion
{
object? Value { get; }
}
}
Hintergrund ist, dass der C# 15.0-Compiler alle Union Types automatisch mit der Annotation [Union] versieht. Das erkennt man, wenn man den Union Type mit ILSpy dekompiliert:
// NET11_Console, Version=11.3.0.0, Culture=neutral, PublicKeyToken=null
// NET11_Console.CS15.PersonOperationResult
using System;
using System.Runtime.CompilerServices;
using NET11_Console.CS15;
[Union]
public struct PersonOperationResult : IUnion
{
public object? Value { get; }
[CompilerGenerated]
public PersonOperationResult(Person? value)
{
Value = value;
}
[CompilerGenerated]
public PersonOperationResult(string value)
{
Value = value;
}
[CompilerGenerated]
public PersonOperationResult(int value)
{
Value = value;
}
[CompilerGenerated]
public PersonOperationResult(Exception value)
{
Value = value;
}
}
Neue und geänderte APIs
In .NET 11.0 Preview 1 hatte Microsoft die Komprimierung mit Zstandard als Alternative zu Deflate, GZip und Brotli eingeführt. In Preview 3 ist diese Implementierung nun Teil der System.IO.Compression.dll und nicht mehr der eigenständigen System.IO.Compression.Zstandard.dll. Zudem gibt es nun eine CRC32-Prüfung der Einträge, sodass fehlerhafte Archive schnell auffallen. In ASP.NET Core lässt sich Zstandard nun für die Komprimierung für HTTP verwenden. Das erfordert aber eine manuelle Aktivierung:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure(options =>
{
options.CompressionOptions = new ZstandardCompressionOptions
{
Quality = 6 // 1-22, higher = better compression, slower
};
});
Im Objekt-relationalen Mapper Entity Framework Core gibt es eine neue Methode GetEntriesForState() in der Klasse ChangeTracker. Hiermit kann man sich alle Objekte liefern lassen, die sich in bestimmten Zuständen (Added, Modified, Deleted, Unchanged) befinden, beispielsweise
Im Gegensatz zu der vorher schon verfügbaren Operation Entries(), die diese Informationen ebenfalls bereitstellt, ruft GetEntriesForState() nicht vorher die Methode DetectChanges() auf, die in gut gefüllten Kontextinstanzen die Performance drücken kann.
In .NET MAUI gibt es nun einen LongPressGestureRecognizer, um auf längeres Drücken zu reagieren. Dabei kann man die Mindestdauer in Millisekunden angeben, ab wann die Geste als „lang“ gelten soll:
Auch das Landkartensteuerelement
Projektmappenfilter-Dateien mit .NET SDK CLI
Laut Release Notes soll man nun Projektmappenfilter-Dateien nicht nur über Visual Studio, sondern auch per Kommandozeile erstellen können. Microsoft liefert dazu in den Release Notes diesen Kommandozeilencode:
dotnet new slnf --name MyApp.slnf
dotnet sln MyApp.slnf add src/Lib/Lib.csproj
dotnet sln MyApp.slnf list
dotnet sln MyApp.slnf remove src/Lib/Lib.csproj
Im Schnelltest zeigte sich aber, dass hier die Implementierung anders ist, als die Release Notes es anzeigen, denn
dotnet new slnf --name MyApp.slnf
legt eine Datei mit doppelter Dateinamenserweiterung an: MyApp.slnf.slnf. Man muss hier also die Dateinamenserweiterung weglassen, bei den Folgebefehlen muss sie aber stehen. Zudem funktioniert das Microsoft-Beispiel nur, wenn der Filter genau heißen soll wie die Projektmappendatei selbst, nur mit .slnf statt .slnx.
Folgender Code zeigt eine funktionierende Umsetzung mit abweichendem Namen:
$ErrorActionPreference = "stop"
$slnf = "NET11Blazor.slnf"
$slnfWithExtension = "$slnf.slnf"
dotnet new slnf --name $slnf -s www.IT-Visions.de_NET11_Demos.slnx --force
dotnet sln $slnfWithExtension add NET11_BlazorServer/NET11_BlazorServer.csproj
dotnet sln $slnfWithExtension add NET11_BlazorWASMStandalone/NET11_BlazorWASMStandalone.csproj
dotnet sln $slnfWithExtension list
dotnet sln $slnfWithExtension add NET11_BlazorWASMStandalone/NET11_BlazorWASMStandalone.csproj
dotnet sln $slnfWithExtension list
Includes für File-based Apps
In .NET 10.0 hatte Microsoft sogenannte File-based Apps eingeführt, mit denen man C#-Programmcode in einer einzelnen Quellcodedatei direkt ohne Projektmappe und ohne vorheriges Kompilieren ausführen kann. In .NET 11.0 Preview 3 kommt die Erweiterung dieses Features um Include-Dateien. Damit kann man in einer C#-Datei andere Dateien über das neue Konstrukt #:include einbinden:
#:include ./Datenklasse.cs#:include ./Hilfsroutinen.cs
Dafür benötigt man aktuell zusätzlich folgende Zeile:
#:property ExperimentalFileBasedProgramEnableIncludeDirective=true
Visual Studio Code Version 1.115 mit der aktuellen C# Dev Kit Version 3.11.200 meldet, dass der Editor das Feature noch nicht kennt:

Visual Studio Code kennt die Bedeutung von #:include noch nicht (Abb. 2).
Schemamigrationen mit Entity Framework Core
Beim Anlegen einer Datenbankschemamigration mit Add-Migration beziehungsweise dotnet ef migrations add speichert Entity Framework Core in der Snapshot-Datei, die den aktuellen Objektmodellzustand zum Zeitpunkt der Erstellung der Schemamigration repräsentiert, nun den Namen der aktuellen Schemamigration in einer Variablen mit dem Namen LatestMigrationId. Zudem gibt es dort einen Kommentar:
// If you encounter a merge conflict in the line below, it means you need to
// discard one of the migration branches and recreate its migrations on top of
// the other branch. See for more info.
public override string LatestMigrationId => "20260415083524_v9";
Damit ist leichter erkennbar, dass es einen Versionsverwaltungskonflikt bei den Schemamigrationen gibt.

Objektmodell-Snapshot in Entity Framework Core 10.0 (links) vs. Entity Framework Core 11.0 (rechts) (Abb. 3)
Verbesserungen für Async Runtime
Seit .NET 11.0 Preview 2 gibt es die asynchrone Laufzeitumgebung für .NET, die erstmals direkt async und await versteht, ohne dass der Compiler im Hintergrund eine State Machine dafür bauen muss. In Preview 3 hat Microsoft die asynchrone Laufzeitumgebung auch in Verbindung mit der direkten Erzeugung von Maschinencode mit ReadyToRun Images und Native AOT implementiert. Zudem ist die Projekteinstellung nicht mehr notwendig. Um die asynchrone Laufzeitumgebung zu setzen, muss man nur noch setzen.
Ausblick
Weitere Details finden sich in den Ankündigungen zu .NET-Version 11.0 Preview 3 und Visual Studio 2026 Insiders Version 11709.129.
.NET 11.0 soll im November 2026 erscheinen und einen Standard-Term-Support von zwei Jahren erhalten. Bis dahin ist mit vier weiteren Preview-Versionen von Mai bis August sowie jeweils einer Release-Candidate-Version im September und Oktober zu rechnen.
(rme)
Entwicklung & Code
Kommentar: Das Ende der SaaS-Gelddruckmaschine
Als der Hedgefonds TCI jüngst seinen Microsoft-Anteil drastisch zusammenstrich, klang das erst einmal nach einer dieser Meldungen, die im Strom der Finanznachrichten untergehen. Doch diesmal lohnt der zweite Blick. TCI-Gründer Chris Hohn gehörte jahrelang zu den großen Profiteuren des Microsoft-Booms, fast 400 Prozent Kursgewinn seit 2017 sprechen eine deutliche Sprache. Wenn ausgerechnet so ein Investor kalte Füße bekommt, geht es nicht um schwache Quartalszahlen. Dann steht die ökonomische Grundlage der Softwareindustrie zur Debatte.
Weiterlesen nach der Anzeige

Moritz Förster schreibt seit 2012 für die iX und heise online. Er betreut neben dem iX-Channel den Bereich Arbeitsplatz.
Die Sprengkraft der KI-Revolution liegt nicht darin, dass sie eine bestimmte Software ersetzt. Sondern darin, dass sie das Geschäftsmodell dahinter pulverisiert. Willkommen in der SaaSpocalypse.
Das Geschäftsmodell hinter der Lizenzmaschine
Zwei Jahrzehnte lang funktionierte Unternehmenssoftware nach einem stabilen Prinzip. Jede Aufgabe bekam ihre eigene Anwendung: Texte in Word, Tabellen in Excel, Kundendaten in Salesforce, Bildbearbeitung in Photoshop. Unternehmen zahlen Lizenzen pro Nutzer, pro Arbeitsplatz, pro Monat. Je tiefer die Software in die Arbeitsabläufe einsickerte, desto verlässlicher sprudelten Umsatz und Margen. Der Wert lag dabei nie nur im Programmcode, sondern in einer schlichten Tatsache: Menschen mussten ihre Arbeit innerhalb dieser Anwendungen erledigen.
Genau diese Logik bröckelt nun. KI-Assistenten verändern nicht bloß einzelne Funktionen, sondern die Schnittstelle zur Arbeit selbst. Wer heute Copilot, Gemini oder ChatGPT nutzt, klickt sich nicht mehr durch klassische Menüs. Stattdessen formuliert der Nutzer Ziele: „Fasse die wichtigsten Punkte aus diesen Mails zusammen.“ „Bau eine Präsentation aus den Quartalszahlen.“ Die Arbeit wandert Schritt für Schritt vom Menschen zur Maschine. Und je besser diese Systeme werden, desto unwichtiger wird die Anwendung im Hintergrund.
Microsoft sägt am eigenen Ast
Das zeigt sich ausgerechnet bei Microsoft selbst. Seinen Copilot präsentiert der Konzern längst nicht mehr als Zusatzfunktion für Word oder Excel. Das Ziel ist ein Assistent, der quer über alle Anwendungen arbeitet. Der Nutzer redet mit dem Agenten – nicht mehr mit dem Programm. Wird die KI zur Bedienoberfläche der Wissensarbeit, verliert die klassische Anwendung ihre Rolle als zentraler Zugangspunkt. Sie wird zur Infrastruktur.
Weiterlesen nach der Anzeige
Aus Nutzersicht klingt das verlockend. Für die Softwareindustrie ist es eine Bedrohung. Denn Software-as-a-Service-Modelle leben davon, dass jeder Mitarbeiter Zugang zu einzelnen Anwendungen braucht. KI-Agenten lösen diese Kopplung auf. Warum Hunderte Vollzugänge bezahlen, wenn ein paar Agenten einen Großteil der Arbeit erledigen?
Die Börse handelt bereits die nächste Ära
Die Nervosität an den Börsen kommt nicht von ungefähr. Microsoft, Salesforce, Adobe und Oracle verdienen weiterhin Milliarden. Aber die Zweifel wachsen, ob die fetten Margen der SaaS-Ära dauerhaft Bestand haben. Bemerkenswert dabei: Die Disruption entsteht innerhalb der Plattformen selbst. Microsoft demonstriert mit Copilot genau die Entwicklung, die das klassische Softwaremodell untergräbt. Der Konzern treibt die Entkopplung von Nutzer und Anwendung selbst voran.
Börsen handeln spekulative Erwartungen, keine Gegenwart. Für Investoren genügt die Aussicht auf sinkende Preissetzungsmacht, um Bewertungen neu zu sortieren. Die Machtfrage lautet deshalb nicht mehr, wer die beste Software baut. Sondern wer künftig die Bedienoberfläche der Arbeit kontrolliert.
Das erklärt, warum TCIs Teilverkauf mehr ist als ein gewöhnliches Börsensignal. Der Fonds stößt Microsoft-Aktien nicht ab, weil Office schwächelt oder Azure plötzlich Verluste schreibt. TCI reagiert auf eine tiefere Unsicherheit: KI löst eben nicht einfach Programme ab. Sie ersetzt die ökonomische Logik, auf der die Softwareindustrie aufgebaut wurde. Und genau deshalb droht die SaaSpocalypse.
Bei diesem Kommentar handelt es sich um das Editorial der iX 6/2026, die am 22. Mai erscheint.
(fo)
Entwicklung & Code
Fedora Hummingbird Linux: Neue Container-basierte Distribution
Neben viel KI und Virtualisierung gab es auf dem Red Hat Summit auch Neuigkeiten im Bereich der Linux-Distribution: Fedora Hummingbird Linux – auch HummingbirdOS genannt – ist da. Hierbei handelt es sich um eine vollständig Container-basierte Distribution. Gleich mehrere bereits bekannte Projekte vereinen sich hier: Zum einen natürlich Software aus dem Fedora-Universum, aber auch rpm-ostree, das von Fedora CoreOS und auch Silverblue bekannt ist.
Weiterlesen nach der Anzeige
Statt über einen traditionellen Paketmanager kommen Software-Aktualisierungen über sogenannte Transaktionen in das System. Das ist vergleichbar mit einem Git-Commit mit zwei Zuständen: dem aktuellen und dem nächsten. HummingbirdOS greift ebenfalls auf das bootc-Projekt zurück. Dabei geht es um sogenannte „bootfähige“ Container, die mit einem Linux-Kernel und systemd ausgestattet sind. Sie können als normale Container laufen oder über bootc wie ein klassisches Linux starten.
Die letzte Zutat für das neue Fedora Linux ist das Hummingbird-Projekt. Es stellt minimalistische und gehärtete Container bereit. Letzteres bedeutet, dass es idealerweise keine bekannten ungepatchten Sicherheitslücken bezüglich der installierten Software gibt. Ziel ist, dass jeder Container nur eine Funktion hat.
HummingbirdOS ausprobieren
Fedora Hummingbird Linux ist noch in einem frühen Stadium. Interessierte können sich aber schon ein Abbild aus der Container-Registry der Hummingbird-Community herunterladen. Ferner kann man das System auch als virtuelle Maschine betreiben. Dazu muss man das Container-Abbild per bootc-image-builder umwandeln. Das Resultat ist entweder eine virtuelle Festplatte oder Installations-CD. Erstere bindet der Anwender entweder in den Hypervisor oder Cloud-Account ein. Danach startet HummingbirdOS analog zu den anderen virtuellen Maschinen. Die Installations-CD benutzt das bekannte Werkzeug Anaconda.
Bei den von Red Hat angedachten Anwendungsfällen schließt sich der Bogen zurück zur KI. HummingbirdOS soll laut Ankündigung als Plattform für KI-Agenten dienen. Designbedingt bietet es aktuelle Software mit minimaler Angriffsfläche. Unterstützung für Nutzer ist über den „Cooperative Community Support“ geplant. Hier können zahlende Red-Hat-Kunden auch Hilfe für bestimmte Projekte außerhalb des Enterprise-Umfeldes bekommen.
Lesen Sie auch
(fo)
Entwicklung & Code
.NET 11.0 Preview 4: Ein bunter Strauß von API-Erweiterungen
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.
Weiterlesen nach der Anzeige

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
(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.
Viel Neues für den Prozessstart
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()
Weiterlesen nach der Anzeige
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.
(De)-Komprimierung mit Span
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 und ReadOnlySpan 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");
ReadOnlySpan sourceSpan = File.ReadAllBytes(BIGFILEPATH);
Console.WriteLine("Länge=" + sourceSpan.Length);
long maxCompressedLength = ZLibEncoder.GetMaxCompressedLength(sourceSpan.Length);
Span 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");
// 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
Parsen von Hex-Werten
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
}
Prüfung auf Gültigkeit bei UTF8 und UTF16
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 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 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));
Utf8JsonWriter mit abweichenden Einstellungen wiederverwenden
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.
-
Künstliche Intelligenzvor 3 Monaten
Top 10: Die beste kabellose Überwachungskamera im Test – Akku, WLAN, LTE & Solar
-
Social Mediavor 3 MonatenCommunity Management und Zielgruppen-Analyse: Die besten Insights aus Blog und Podcast
-
Entwicklung & Codevor 2 MonatenCommunity-Protest erfolgreich: Galera bleibt Open Source in MariaDB
-
Künstliche Intelligenzvor 2 MonatenBlade‑Battery 2.0 und Flash-Charger: BYD beschleunigt Laden weiter
-
Künstliche Intelligenzvor 2 Monaten
Top 10: Der beste Luftgütesensor im Test – CO₂, Schadstoffe & Schimmel im Blick
-
Apps & Mobile Entwicklungvor 2 MonatenMähroboter ohne Begrenzungsdraht für Gärten mit bis zu 300 m²
-
Social Mediavor 2 MonatenVon Kennzeichnung bis Plattformpflichten: Was die EU-Regeln für Influencer Marketing bedeuten – Katy Link im AllSocial Interview
-
Künstliche Intelligenzvor 2 MonateniPhone Fold Leak: Apple spart sich wohl iPad‑Multitasking
