C15 Union Types: The Pattern Matching Feature Finally Here
C# 15 Union Types: The Pattern Matching Feature Finally Here
TL;DR: C# 15 introduces union types - a compiler-enforced way to represent values that can be one of several types. With exhaustive pattern matching and no inheritance requirements, this feature replaces result patterns, marker interfaces, and complex error handling.
If you've been waiting for C# to catch up with functional programming features, your wait is finally over. C# 15 introduces union types - a language feature that's been requested for years and is now part of .NET 11 preview.
Microsoft recently announced this feature as part of C# 15, and it's already making waves in the developer community. Here's everything you need to know about this game-changing addition.
The Problem This Solves
For years, C# developers have faced limited options when representing values that can be multiple distinct types:
- Generic
Result<T, E>wrappers - Exception handling (even for predictable errors)
- Inheritance hierarchies with marker interfaces
Union types offer a cleaner, more explicit alternative with compile-time exhaustiveness checking.
Syntax and Basics
Declare with the union keyword:
public union Pet(Cat, Dog, Bird);
This creates a Pet union holding a Cat, Dog, or Bird. The compiler ensures every switch handles all three cases.
Real Example: Authentication
public union AuthResponse(User authenticatedUser, string errorMessage);
AuthResponse Authenticate(string token)
{
if (IsValidToken(token))
{
var user = GetUserFromToken(token);
return new AuthResponse(user, null!);
}
return new AuthResponse(null!, "Invalid or expired token");
}
Exhaustive Pattern Matching
The real power comes from compile-time safety:
AuthResponse response = Authenticate(userToken);
switch (response)
{
case AuthResponse.User user:
Console.WriteLine($"Welcome back, {user.Name}!");
break;
case AuthResponse.ErrorMessage error:
Console.WriteLine($"Error: {error.Message}");
break;
}
Key point: The compiler requires both cases. Miss one? Compile-time error. No runtime surprises.
Error Handling Without Exceptions
Replace "exceptions as control flow" with explicit error types:
// Traditional
public User GetUser(string id)
{
var user = _repository.Find(id);
if (user == null)
throw new UserNotFoundException(id);
return user;
}
// Union type
public union GetUserResult(User user, UserNotFoundException error);
Errors become first-class citizens rather than hidden control flow.
Three Primary Use Cases
1. Result-or-Error Returns
public union ProcessResult(T success, Error error);
Perfect for operations with two distinct outcomes.
2. Message Dispatching
public union Command(Login, Logout, UpdateProfile);
Adds new command? You'll get warnings if unhandled everywhere.
3. Replacing Marker Interfaces
// Old
public interface IErrorResponse { }
// New
public union ApiResponse(Data, ErrorResponse);
Performance
According to Microsoft:
- Zero runtime overhead - compiles to same IL
- Better type safety at compile time
- Smaller code footprint - no inheritance hierarchies
Migration from Common Patterns
From Try-Pattern
// Old
bool TryParse(string s, out int result)
// New
union ParseResult(T int value, ParseError error)
From Result
// Old
public struct Result<T, E> { ... }
// New
public union Result<T>(T value, E error)
Best Practices
- Keep cases small (2-10 typically)
- Use descriptive names for clarity
- Document error cases with XML docs
- Group related errors together
- Rely on compiler warnings for missing cases
Migration Path
TryXxxpatterns → Union types with explicit error states- Generic
Result<T, E>→ Native union syntax - Marker interfaces → Direct unions
Conclusion
Union types bring C# closer to F# and Haskell's type safety while maintaining C#'s pragmatic approach. Explicit, safe, and zero-overhead.
Try it: Use Visual Studio 2026 Insiders or .NET 11 preview SDK.
💡 About the author: https://www.linkedin.com/in/vikrant-bagal
#dotnet #csharp #programming #softwareengineering #webdev
[^1]: Microsoft Learn - Union Types Documentation [^2]: .NET Blog - C# 15 Union Types Announcement [^3]: C# Language Specification (preview for C# 15)




