Discriminated Unions in C#: Was der .NET-Experte erklärt — und was dein KMU-Entwicklerteam davon wirklich braucht

Maarten Balliauw zeigt einen eleganten Trick mit implicit operators und dem OneOf-Package. Ich erkläre, wann das in einem Mittelstandsteam mit 3-8 Entwicklern tatsächlich Sinn ergibt — und wann es Komplexität kostet, die du nicht brauchst.

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

Vor einem halben Jahr saß ich bei einem Maschinenbauer aus dem Stuttgarter Raum. 180 Mitarbeitende, sechs Entwickler, eine gewachsene .NET-Anwendung seit 2014. Der Lead-Entwickler zeigt mir Methode um Methode, die null zurückgibt — oder eine Exception wirft — oder einen Boolean-Flag setzt — je nachdem, was schiefgelaufen ist. Drei verschiedene Konventionen in derselben Codebase, gewachsen über vier Entwicklergenerationen. "Wir wissen nie genau, was eine Methode eigentlich zurückgeben kann, bis wir sie aufrufen", sagt er. Ich kenne diesen Satz. Ich höre ihn ungefähr einmal pro Quartal.

Genau dieses Problem — "was kann diese Methode eigentlich zurückgeben?" — ist der Kern von Discriminated Unions. Und deshalb hat mich der Artikel von Maarten Balliauw interessiert.


Was Maarten erklärt

Maarten liefert einen klaren, technisch präzisen Einstieg in das Thema. Die Grundidee: Du willst dem Compiler sagen, dass eine Methode genau einen von N definierten Typen zurückgeben kann — nicht mehr, nicht weniger. F# hat das seit Jahren eingebaut, C# noch nicht nativ. Maarten zeigt, wie ASP.NET Core Minimal APIs das mit der Results<TResult1, TResult2> Klasse und implicit operators nachbauen. Er erklärt dann, wie man denselben Trick im eigenen Code anwenden kann — und weist direkt auf die Schwäche hin: Wenn du später einen vierten Rückgabetyp hinzufügst, bekommst du keinen Compiler-Fehler in deinen switch-Ausdrücken. Exhaustive Matching fehlt. Als Lösung empfiehlt er das OneOf-Package, das dieses Gap schließt.

Solider Artikel, fairerweise. Technisch korrekt, die Schwächen werden nicht versteckt, und der Quelltextblick in die ASP.NET Core Interna ist echten Mehrwert wert.


Wie das in einem KMU wirklich landet

Jetzt kommt meine Perspektive — und die ist weniger aus der Sprach-Feature-Ecke als aus der "wie überlebt das drei Entwicklerwechsel"-Ecke.

Erster Reality-Check: Wer nutzt Minimal APIs?

In meinen Mandaten — typisch 30 bis 300 Mitarbeitende, IT-Abteilung von 2 bis 12 Personen — sehe ich immer noch überwiegend klassische Controller-basierte ASP.NET Core APIs. Minimal APIs sind interessant, aber die Migration läuft langsam. Das bedeutet: Der elegante Results<Ok, Unauthorized> Trick aus dem Artikel ist für viele Mittelstandsteams erst mal akademisch. Nicht weil sie rückständig wären, sondern weil eine Migration zu Minimal APIs kein Selbstzweck ist.

Zweiter Reality-Check: Das OneOf-Package und die Einführungskosten

OneOf löst das Exhaustive-Matching-Problem. Das ist technisch richtig. Aber ich frage immer: Was kostet die Einführung eines neuen Patterns in einem Team ohne dedizierten Architekten?

In einem Team mit vier bis acht Entwicklern, von denen zwei Senior-Level sind und der Rest irgendwo zwischen Junior und erfahrenem Mittelbau liegt, bedeutet jede neue Abstraktion Schulungsaufwand, Code-Review-Overhead und — das Entscheidende — kognitive Last bei jedem künftigen Entwickler, der den Code liest. OneOf ist nicht schwierig. Aber OneOf<User, UserAlreadyExists, InvalidUsername> ist für jemanden, der das Pattern nicht kennt, erstmal ein Fragezeichen. Das Fragezeichen kostet Zeit. Zeit kostet Geld.

Ich sage nicht, dass es sich nicht lohnt. Ich sage: Den Trade-off musst du bewusst machen.

Was wirklich hilft — und was ich tatsächlich empfehle

Das Problem, das Discriminated Unions lösen, ist real. In meinen Mandaten lösen die meisten Teams es mit einer von drei Ansätzen:

  1. Exceptions für Fehlerfälle — funktioniert, aber bricht den Control Flow und ist in .NET teuer in heißen Pfaden
  2. Custom Result-Klassen — selbstgebastelt, oft inkonsistent, nach zwei Jahren eine kleine Zoo-Landschaft
  3. Nichtsnull und Hoffnung

Was ich in der Praxis häufig empfehle, bevor ich zu OneOf greife: ein einfaches generisches Result<T> Pattern mit einem dedizierten Error-Typ. Das ist kein neues Package, das erklärt werden muss — das sind zwei Klassen, die jeder .NET-Entwickler in 10 Minuten versteht:

public record Result<T>(T? Value, string? Error, bool IsSuccess);

Damit löst du 80% der "was gibt diese Methode zurück?"-Probleme in einem KMU-Kontext. Es ist nicht so ausdrucksstark wie echte Discriminated Unions, und der Compiler hilft dir nicht bei Exhaustiveness — aber es ist sofort verständlich, es braucht kein Package, und neue Teammitglieder brauchen keine Einführung.

Wann lohnt sich OneOf trotzdem?

Wenn du ein Team hast, das bereits mit funktionalen Mustern vertraut ist. Wenn ihr regelmäßig Code-Reviews macht und Patterns aktiv kommuniziert werden. Wenn du mehr als, sagen wir, sechs Rückgabefälle in einer Domain-Methode hast. Und wenn der Lead-Entwickler eine Antwort auf "warum OneOf statt Custom Result?" formulieren kann, die jeder im Team versteht.

Das sind nicht viele KMUs. Aber es sind welche. Ich habe zwei Mandate pro Jahr, wo OneOf die richtige Wahl wäre. Drei, wenn ich großzügig zähle.

Was im Artikel fehlt aus Mittelstandssicht

Maarten schreibt für ein Publikum, das sich in C# und .NET-Interna wohlfühlt. Das ist sein Publikum, und er bedient es gut. Was fehlt, ist die organisatorische Frage: Wer im Team owned die Entscheidung, ein neues Pattern einzuführen? Wer schreibt die ADR dazu? Wie testet ihr, dass der nächste Entwickler, der in sechs Monaten an den Code kommt, sofort versteht, was da passiert?

Das sind keine technischen Fragen. Das sind Fragen über Team-Reife und Kommunikation. Und in meiner Erfahrung entscheidet die Antwort auf diese Fragen mehr über den Erfolg eines neuen Coding-Patterns als die technische Eleganz des Patterns selbst.


Meine Empfehlung in einem Satz

Wenn du Methoden hast, die mehr als zwei verschiedene "Erfolgszustände" zurückgeben können, fang mit einem simplen Result<T>-Pattern an — und evaluiere OneOf erst, wenn du merkst, dass dein Team aktiv gegen die Grenzen dieses einfachen Patterns läuft.

Maartens Artikel ist der richtige nächste Schritt, wenn du an diesen Punkt kommst.


Der vollständige Original-Beitrag von Maarten Balliauw: Discriminated Unions in C#


Du arbeitest mit einem .NET-Team und fragst dich, ob OneOf oder ein Custom-Result-Pattern der richtige Schritt ist? Ich sage dir in 30 Minuten, was in eurem spezifischen Kontext Sinn ergibt — ohne Produktpitch.

30 Min Klartext-Sparring

— Bernhard