# 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:

```csharp
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

```csharp
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:

```csharp
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:

```csharp
// 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
```csharp
public union ProcessResult(T success, Error error);
```

Perfect for operations with two distinct outcomes.

### 2. Message Dispatching
```csharp
public union Command(Login, Logout, UpdateProfile);
```

Adds new command? You'll get warnings if unhandled everywhere.

### 3. Replacing Marker Interfaces
```csharp
// 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
```csharp
// Old
bool TryParse(string s, out int result)

// New
union ParseResult(T int value, ParseError error)
```

### From Result<T, E>
```csharp
// 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)

