C# 15 preview, shipping with .NET 11 Preview SDKs, introduces two related features: nominal union types (an IUnion-backed struct wrapping a fixed set of existing types) and a closed modifier for exhaustive class/record hierarchies. Both give compiler-checked exhaustiveness in switch expressions, but union types are unions of pre-existing types, not F#-style discriminated unions with inline case data, and any value-type case gets boxed unless you hand-write a non-boxing IUnionMembers implementation. The closed modifier is argued to be the lower-risk, more immediately useful feature since it adds no new runtime type or boxing concerns. Both features require LangVersion=preview, remain under active design (only 4 of 8 sub-proposals closed on the tracking issue), and a widely cited November 2026 GA date is unconfirmed on an official Microsoft schedule.

9m read timeFrom daily-devops.net
Post cover image
Table of contents
What Shipped, and WhereThe Catch: Unions of Types, Not of CasesThe Underrated Half: closed HierarchiesMy Expectation, and the RiskPractical Takeaway

Questions this post answers

Does C# 15's new union type work like F# discriminated unions?

No. C# unions declared with the union keyword are unions of pre-existing, independently usable types, not discriminated unions with inline case data as in F#. A declaration like union Pet(Cat, Dog, Bird) wraps existing Cat, Dog, and Bird types in a compiler-generated struct implementing IUnion, rather than defining cases and their fields together as part of the type itself. Following how new language features actually behave versus their pitch helps teams pick idioms with confidence, something daily.dev streamlines.

Does C# 15's union type cause boxing for value types?

Yes. The compiler-generated struct behind a one-line union declaration always stores its contents as object?, so any value-type case, such as int or double, gets boxed on every access. Avoiding this requires abandoning the one-line syntax and hand-writing the union struct with IUnion, a HasValue property, and per-case TryGetValue(out T) overloads instead. Teams weighing allocation trade-offs before adopting a preview C# feature can track details like this on daily.dev.

What is the difference between C# 15's closed modifier and union types?

The closed modifier marks an existing class or record hierarchy as a complete, compiler-enforced set of subtypes, giving the same exhaustiveness diagnostics (CS8509, CS8510) as union but without a new runtime type, an IUnion interface, boxing concerns, or a null-arm surprise. Use closed for behaviorally distinct types with their own methods and inheritance; use union for interchangeable data shapes switched over. Deciding which C# 15 idiom fits a given domain shape is easier when tracking language proposals on daily.dev.

4 Impressions