Letzten Herbst saß ich bei einem Maschinenbauer in der Pfalz. Acht Entwickler, gewachsenes .NET-Backend seit 2018, und ein frisch übernommener Entwickler hatte — besten Absichten — eine neue Klasse von einer internen Basisklasse abgeleitet, die eigentlich nie von außen extended werden sollte. Niemand hatte es dokumentiert. Der ursprüngliche Entwickler war längst weg. Drei Wochen später fiel ein Report-Modul mit einer NullReferenceException aus, die sich niemand erklären konnte — weil die neue Ableitung einen kritischen Initialisierungsschritt in der Basisklasse umging, der nur durch Konvention abgesichert war.
Das ist kein Einzelfall. In fast jedem Mittelstands-Team, das ich in den letzten Jahren begleitet habe, gibt es solche impliziten Verträge im Code. Meistens in Kommentaren wie // NICHT ableiten! oder gar nicht. Genau da setzt das neue closed-Keyword in .NET 11 an — und deshalb hat mich Andrew Locks Artikel mehr interessiert als die übliche Preview-Rundleuchte.
Was Andrew Lock beschreibt
Lock erklärt das closed-Keyword präzise und ohne Umwege: Eine closed class kann nur innerhalb desselben Assembly abgeleitet werden. Wer in einem anderen Assembly eine Kindklasse bauen will, bekommt einen Compilerfehler. Das Entscheidende ist aber nicht die Zugriffskontrolle allein — das war mit private protected-Konstruktoren vorher schon möglich, wenn auch hässlich. Der eigentliche Gewinn ist, dass der Compiler jetzt weiß, dass die Hierarchie abgeschlossen ist. Das ermöglicht echte Exhaustiveness Checks in Switch Expressions: Kein erzwungener Catch-All mehr, der eine neue Ableitung stillschweigend verschluckt und zur Laufzeit mit einer InvalidOperationException explodiert. Wer ein neues Animal hinzufügt und vergisst, alle Switch Expressions anzupassen, bekommt sofort einen Compilerfehler — nicht erst drei Wochen später auf Produktion.
Wie das in einem KMU-Team wirklich landet
Lass mich direkt sein: Der Artikel ist aus der Perspektive eines Framework-Autors oder Library-Maintainers geschrieben. Andrew Lock denkt in Assemblies, die von fremden Teams konsumiert werden. Das ist eine legitime Perspektive — aber sie ist nicht die, die du als Entwickler bei einem Hersteller von Fertigungssteuerungssoftware oder einem mittelständischen SaaS-Anbieter täglich lebst.
Was sich lohnt: Das Exhaustiveness-Argument ist für KMU-Teams sogar relevanter als für Framework-Autoren. Warum? Weil du in einem 8-Personen-Team keine strikte Code-Review-Kultur mit mehreren Reviewern hast, die sicherstellt, dass jeder neue Typ korrekt in allen Switch Expressions berücksichtigt wird. Du verlässt dich auf den Compiler. Und genau da ist closed ein echter Gewinn. Ich schätze, dass in Teams dieser Größe mindestens 30-40 % der "vergessene-Branch"-Bugs in Switch Expressions entstehen, die ursprünglich mal vollständig waren und durch spätere Ergänzungen inkomplett wurden. Das ist kein spezifisches .NET-Problem — das Pattern zieht sich durch jede Sprache.
Das closed-Keyword macht implizite Architekturentscheidungen explizit. "Diese Hierarchie ist abgeschlossen, wir haben hier alle Fälle kontrolliert" ist jetzt eine maschinenlesbare Aussage, keine Konvention im Wiki. Das ist für Teams, die keinen dedizierten Architekten haben, der täglich Code reviewt, ein substanzieller Unterschied.
Was nicht lohnt — oder was fehlt: Lock erwähnt selbst kurz den Vergleich zu Discriminated Unions in F# und anderen funktionalen Sprachen. Was er nicht ausspricht: Wenn du heute in C# typsichere, exhaustive Wertehierarchien brauchst, bist du seit .NET 5 mit record-Typen und seit .NET 7 mit dem verbesserten Pattern Matching in Switch Expressions bereits gut bedient — solange du die Hierarchie im selben Assembly hältst. Das closed-Keyword löst das Cross-Assembly-Problem, aber ehrlich gesagt: Wie viele KMU-Teams haben ein echtes Cross-Assembly-Vererbungsproblem bei ihren Domänenklassen?
In meiner Beratungspraxis: selten. Was häufiger vorkommt, ist das intra-Assembly-Problem — eine Klasse, die zwar im selben Projekt ist, aber trotzdem nicht weiter abgeleitet werden sollte, weil die interne Logik darauf angewiesen ist. Dafür hat man bisher sealed genutzt. closed ist semantisch etwas anderes — es versiegelt nicht die Klasse selbst, sondern die Hierarchie nach außen — aber in der täglichen KMU-Praxis ist die Verwechslungsgefahr real. Ich sehe schon die Pull-Request-Kommentare: "Warum closed und nicht sealed?" Das wird zunächst Erklärungsaufwand erzeugen.
Was in Locks Artikel fehlt: eine klare Antwort auf die Frage, wann man closed kombiniert mit sealed nutzen sollte und wann nicht. Eine closed abstract class mit konkreten, gesealten Kindklassen — das ist das Pattern, das ich in vielen Domänenmodellen sehe und das jetzt erheblich sauberer ausgedrückt werden kann. Dieser Anwendungsfall hätte eine eigene Sektion verdient.
Meine konkrete Empfehlung
Für KMU-Teams, die auf .NET 10 oder früher sind: Das ist kein Migrationsgrund. Aber es ist ein guter Zeitpunkt, im nächsten Architecture Review zu fragen: "Welche Basisklassen in unserem Code haben eigentlich den impliziten Vertrag 'nicht von außen ableiten'?" Wenn die Antwort mehr als drei sind und sie nur durch Kommentare oder Konvention geschützt werden, dann habt ihr bereits heute ein technisches Schulden-Problem — das closed-Keyword wird euch 2026 oder 2027 dabei helfen, es zu benennen.
Für Teams, die gerade neu aufsetzen oder eine größere Refaktorierung planen: Wenn ihr Domänenklassen habt, die als Discriminated-Union-Ersatz in Switch Expressions genutzt werden — Statusmodelle, Eventtypen, Command/Query-Hierarchien — dann ist closed in Kombination mit exhaustiven Switch Expressions das sauberste Pattern, das ihr in C# aktuell haben könnt. Besser als der private protected-Hack, besser als der erzwungene Catch-All mit throw.
Eine Zahl aus der Praxis: In einem Logistik-SaaS, das ich letztes Jahr begleitet habe, haben wir 23 Switch Expressions identifiziert, die über einen _ => throw new NotImplementedException() abgesichert waren. In vier davon hatte ein neuer Eventtyp über 14 Monate unbemerkt zu einer toten Code-Pfad-Situation geführt — der Catch-All hatte den Bug verborgen, statt ihn aufzudecken. Mit einem geschlossenen Typsystem hätten diese vier Fälle als Compilerfehler aufgeschlagen, bevor sie in Production gingen.
Das ist der eigentliche Wert von closed. Nicht das schönere Syntax, nicht das elegantere Modell — sondern dass der Compiler jetzt als Sicherheitsnetz für eine Klasse von Bugs dient, für die Teams bisher auf Disziplin, Reviews oder Glück angewiesen waren.
Der vollständige Original-Beitrag von Andrew Lock: Closed class hierarchies: Exploring the .NET 11 preview - Part 4
Wenn du wissen willst, ob closed, sealed, Records oder Discriminated Unions das richtige Pattern für euer Domänenmodell sind — ohne dass ich dir drei Optionen hinwerfe und sage "kommt drauf an":
— Bernhard