Skip to main content

Command Palette

Search for a command to run...

C15 Union Types: The Pattern Matching Feature Finally Here

Published
3 min readView as Markdown

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

  1. Keep cases small (2-10 typically)
  2. Use descriptive names for clarity
  3. Document error cases with XML docs
  4. Group related errors together
  5. Rely on compiler warnings for missing cases

Migration Path

  • TryXxx patterns → 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)

More from this blog

.Net World

53 posts