C# 15 bekommt Union Types — und dein Team braucht sie wahrscheinlich noch nicht

C# 15 schließt eine jahrelange Lücke mit echten Discriminated Unions. Maarten Balliauw erklärt das Feature exzellent. Ich erkläre, warum der Mittelstand trotzdem erst mal Hausaufgaben hat.

⚙ KI-Entwurf Dieser Beitrag wurde automatisiert generiert und nicht redaktionell freigegeben. Behandelt ihn entsprechend.

Letzten Herbst war ich bei einem Maschinenbauer, 80 Mitarbeitende, Stammmannschaft von vier Entwicklern. Die hatten über drei Jahre eine interne Auftragsplattform aufgebaut — solides .NET-Handwerk, keine Spielereien. Dann kam das Ticket: „Bestellstatus-API gibt manchmal null zurück, manchmal wirft sie eine Exception, und manchmal kommt ein leeres Objekt." Drei verschiedene Entwickler, drei verschiedene Interpretationen, was „kein Ergebnis" bedeutet. Keine böse Absicht — einfach fehlende Sprachunterstützung, die sich über Jahre als technische Schuld aufgestaut hatte.

Genau dafür sind Discriminated Unions das richtige Werkzeug. Und genau dafür hätte ich sie damals gebraucht — nur dass es sie in C# damals noch nicht gab.

Was Maarten Balliauw beschreibt

Maarten hat einen sehr sauberen Überblick über das neue union-Keyword in C# 15 geschrieben, das mit .NET 11 Preview kommt. Das Feature schließt eine Lücke, die C#-Entwickler seit Jahren mit Workarounds wie dem OneOf-NuGet-Package, ASP.NET Core's Results<> oder händisch implementierten implicit operator-Überladungen gestopft haben. Der Kernpunkt: Du deklarierst mit einer Zeile, dass ein Typ genau einer von mehreren klar definierten Typen ist — public union Pet (Cat, Dog, Bird) — und der Compiler stellt im Pattern Matching sicher, dass du alle Fälle abdeckst. Vergisst du einen Fall, kommt CS8509. Fügst du drei Monate später einen vierten Fall hinzu, zeigt dir der Compiler exakt, welche switch-Ausdrücke du anpassen musst — kein defensives _ => throw new InvalidOperationException("unreachable") mehr als Sicherheitsnetz.

Das ist kein akademisches Feature. Wer schon mal eine Methode wie RegisterUser() gebaut hat, die entweder einen User, einen UserAlreadyExists-Fehler oder ein InvalidUsername-Problem zurückgeben soll, kennt den Schmerz. Jetzt geht das sauber: ohne Bibliothek, ohne Boilerplate, mit Compiler-Garantie.

Wie das im KMU wirklich landet

Kurz: Das Feature ist richtig gut. Aber der Weg bis zum produktiven Einsatz in einem typischen Mittelstands-Dev-Shop ist länger, als der Artikel impliziert.

Das Versionsthema. .NET 11 ist Preview. Der Mittelstand ist da erfahrungsgemäß konservativ — und das ist nicht irrational. Bei meinen Mandanten läuft der Durchschnitt aktuell auf .NET 6 bis .NET 8. Nicht weil die Entwickler rückständig sind, sondern weil Migration immer gegen laufende Featurewünsche konkurriert. Nach meiner Erfahrung vergehen in KMU-Umgebungen ohne dediziertes Plattform-Team 18 bis 24 Monate, bis ein neues LTS-Release produktiv ist — und .NET 11 ist kein LTS. Wer heute mit einem Greenfield-Projekt auf .NET 9 oder 10 startet, sollte dieses Feature auf dem Radar haben. Wer eine laufende Anwendung betreibt, wartet auf .NET 10 LTS und schaut dann, ob das Team die Kapazität hat, C# 15 Features sauber einzuführen.

Das Skill-Thema. Union Types und exhaustives Pattern Matching sind kein schwieriges Konzept — wenn man es kennt. Aber die meisten Mittelstands-Teams setzen sich nicht wochentäglich mit Typsystem-Features auseinander. Ich sehe regelmäßig Teams, die switch-Statements noch als Statement schreiben, nicht als Expression. Die records erst seit einem Jahr einsetzen. Die nullable reference types zwar eingeschaltet haben, aber ein Drittel der Warnungen mit ! wegdrücken — weil der Lieferdruck größer ist als der Qualitätsdruck. Das ist kein Vorwurf, das ist der normale Alltag in einem Team, das hauptsächlich Features baut, nicht Sprachfeatures evaluiert. Union Types kommen da obendrauf. Und wenn der neue Junior-Entwickler im Herbst anfängt und die Codebase voller union-Deklarationen sieht, braucht er Onboarding-Zeit, die eingeplant werden muss.

Was fehlt im Artikel. Maartens Beispiele sind sauber aus .NET-Perspektive: OrderPlaced, OrderShipped, OrderCancelled als Union modelliert, exhaustives Pattern Matching im Event Handler. Das ist der richtige Weg. Was fehlt, ist die Frage davor: Wie einigt man sich im Team überhaupt darauf, welche Fehler als Union-Cases modelliert werden und welche als Exceptions? Das ist kein C# 15 Problem — das ist eine Architekturentscheidung, die die meisten KMU-Teams nie explizit getroffen haben. Ich erlebe regelmäßig Codebasen, wo die Hälfte der Fehlerbehandlung über Exceptions läuft, ein Viertel über null-Returns und ein Viertel über bool-Returns mit out-Parametern. Union Types lösen das technisch, aber sie ersetzen nicht die Konvention, die das Team vorher braucht.

Konkrete Empfehlung

Für Teams auf .NET 6, 7 oder 8 — das ist der größte Teil meiner Mandanten: Führt das Result<T, E>-Pattern jetzt ein, mit OneOf oder einer einfachen eigenen Implementierung. Startet bei neuen Services oder Modulen, nicht durch Refactoring bestehender Code. Schreibt intern fest, welche Fehlertypen als Union-Cases modelliert werden und welche als Exceptions — das ist die eigentliche Arbeit, nicht das Tooling. Der Code wird besser lesbar, die Fehlerbehandlung expliziter, und die spätere Migration zu nativen Union Types ist dann eine mechanische Suchen-und-Ersetzen-Aktion, keine Denkaufgabe.

Für Teams, die heute auf .NET 9 oder 10 starten: Schaut euch die C# 15 Preview Features an, plant aber explizit ein, welche neuen Features ihr wann einführt — nicht alles auf einmal.

Was ich nicht empfehle: das Feature als Hype-Argument nutzen, um ein Migrationsprojekt zu rechtfertigen. „Wir brauchen .NET 11, weil Union Types" ist keine Argumentation, die vor dem CFO standhält. „Wir haben in den letzten 12 Monaten drei kritische Bugs durch unklare Rückgabetypen gehabt, und hier ist die strukturelle Lösung" — das schon.


Der vollständige Original-Beitrag von Maarten Balliauw: Discriminated unions in C# and .NET 11 (for real this time)


Wenn du wissen willst, ob und wie das für dein Team gerade relevant ist — ohne Produktpitch, ohne Berater-Blabla:

30 Min Klartext-Sparring

— Bernhard