
After years of workarounds, competing proposals, and a community that kept asking every cycle like a kid checking if Santa came early, C# 15 ships the union keyword in .NET 11. The Hacker News post landed on the front page today with 300+ points — a number that says everything about how long the .NET ecosystem has been waiting. The feature is available now in .NET 11 Preview 4, which shipped on May 12, and it changes how C# developers model C# union types for good.
What C# Union Types Actually Give You
A union type in C# 15 lets a variable hold exactly one of a fixed set of types — and the compiler enforces that you handle every case. No casting. No silent null checks. No runtime surprises when someone adds a new case six months later and forgets to update every call site.
Here is the use case developers have wanted for error handling since forever:
public union ApiResult<T>(T, ApiError, ValidationFailure);
// Every call site must handle all three cases — or it will not compile
string message = result switch
{
T value => $"Got: {value}",
ApiError err => $"API failed: {err.Message}",
ValidationFailure v => $"Invalid: {v.Field}",
};
That switch expression has no default arm — and it does not need one. The compiler knows exactly which types are possible and will throw a compile error if you miss a case, or if someone adds a new case to ApiResult without updating every handler. That guarantee is what marker interfaces and abstract base classes could never provide: a truly closed set.
The Years of Workarounds Are Finally Over
The OneOf library became popular precisely because the language did not have this. It worked, but the tradeoffs were real: value types got boxed, IDE support for exhaustiveness was unreliable, and you were always one missing case away from a runtime MatchFailureException. Marker interfaces were worse — any assembly could implement them, so the set was never actually closed, and the compiler never warned you about incomplete matching.
Discriminated union proposals have circled the csharplang GitHub repository since around 2020. Multiple language design meetings in 2022, 2024, and 2025. One community analysis called the absence “a feature users sorely miss and cannot really replicate using other means.” Today, that changes. The C# 15 union types announcement on the official .NET Blog describes it as “the single biggest type-system addition to C# since nullable reference types.”
Worth reading alongside that: Microsoft shipped C# 16’s unsafe rewrite this week too, making it a particularly dense month for C# language changes.
The Boxing Gotcha — Know Before You Commit
Here is the honest limitation the HN thread has already surfaced: the standard union implementation stores its value as object?. That means value types — int, any struct — get boxed on assignment. For most business logic, this is irrelevant. However, for high-throughput, low-allocation code on hot paths, it matters.
The escape hatch exists: implement TryGetValue<T>() methods on your union with the [Union] attribute, which lets the compiler pattern-match without boxing. It is more boilerplate, but it is the right tool for performance-sensitive scenarios. The official announcement documents the pattern clearly — read it before dropping union types into any hot path.
Is This F# Discriminated Unions? Not Quite — and That Is Fine
F# defines case types inline:
type Shape =
| Circle of float
| Rectangle of float * float
C# composes existing types instead:
// Circle, Rectangle, and Triangle are already-defined types
public union Shape(Circle, Rectangle, Triangle);
The HN thread has the predictable contingent arguing this is not “real” discriminated unions. They are not wrong that the semantics differ — C# unions do not carry inline data per variant the way F# or Rust enums do. However, the C# design is deliberate. It integrates with an existing OO codebase where types are already defined, rather than requiring developers to rethink their type hierarchy from scratch. Moreover, calling it lesser misses the design constraint. For the C# audience and the millions of lines of existing .NET code, this is the correct tradeoff.
How to Try It Today
Union types are live in .NET 11 Preview, available from dotnet.microsoft.com. Create a project targeting net11.0, add <LangVersion>preview</LangVersion> to your .csproj, and the union keyword works. IDE support landed in Visual Studio 2026 Insiders and the C# Dev Kit for VS Code as of Preview 3 in April.
The final .NET 11 release is November 2026. The Preview 4 release notes show the runtime stabilizing fast — runtime-async improvements and JIT enhancements shipped alongside. If you have been waiting for a reason to look at what .NET 11 brings, this is it. Andrew Lock’s deep-dive on union types is the HN article worth reading alongside the official docs if you want to understand the boxing behavior before committing.













