C#

Closed hierarchies in C# 15: exhaustive pattern matching for class types

C# 15 adds the closed modifier, which limits derived types of a base class to the same assembly. The compiler then knows the full list of subtypes and can flag switch expressions that miss a case. This helps .NET developers catch new cases at build time instead of finding silent runtime bugs in game logic or domain rules.

Published
Reading time
12 min read
agent run #7
agent run #7
Go to original blog.nimblepros.com

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • Add closed to the base type; closed types are implicitly abstract, so writing abstract on them is an error.
  • Only types in the same assembly can derive from a closed type; deriving from another assembly gives CS9382.
  • You can remove the discard arm from switch expressions, and adding a new subtype then breaks the build until it is handled.
  • Only switch expressions are checked; switch statements are not flagged for exhaustiveness.
  • Seal the subtypes. A direct subtype left open lets other assemblies derive from it.
  • The feature requires C# 15; in a C# 14 project the compiler reports CS9327.

This post explains the closed modifier that C# 15 brings to class hierarchies, using a small board game as the running example. An open base class forces a discard arm in switch expressions, so a newly added subtype can slip through unnoticed. With closed, only types in the same assembly can derive from the base, and the compiler knows the complete list, so it can report non-exhaustive switches as errors.

The author also covers the rules: closed cannot be combined with abstract, closed interfaces are not supported, and derived types cannot come from another assembly. Making subtypes sealed is recommended, and nested closed types let you group related cases. The article pairs closed hierarchies with unions from the previous part, and it notes that the feature needs C# 15 and was verified on the .NET 11 RC1 SDK.

Real-world use

  • A rules-driven domain model, such as a board game or workflow engine, where every victory or validation switch must handle a newly added rule.
  • An order status or payment outcome hierarchy owned entirely by one solution, where the complete list of cases is known and should be enforced by the compiler.

Why it matters

A new subtype added to an open base class can silently take the wrong path through a catch-all arm. With closed, the compiler catches it at build time, but you must accept that only your own assembly can extend the type.

This summary was generated by the agent from the original post on NimblePros Blog. The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message