DEV Community

Vikrant Bagal
Vikrant Bagal

Posted on

C15 Union Types in .NET 11: A Game-Changer for Type Safety!

C# 15 Union Types in .NET 11: A Game-Changer for Type Safety!

🚀 Microsoft just shipped C# 15 in .NET 11 Preview 1, and it includes one of the most requested features ever: Union Types!

🤔 What Are Union Types?

Union types allow you to declare that a variable can only hold one value from a closed set of types. Instead of juggling marker interfaces, inheritance hierarchies, or object-based patterns, you can simply write:

union Pet { Cat cat, Dog dog, Bird bird }

Pet p = Pet.cat(new Cat()); // Valid
Pet p2 = Pet.dog(new Dog()); // Valid
Pet p3 = Pet.bird(new Bird()); // Valid
// Pet unknown = Pet.cat(null); // Compiler-enforced nullability!
Enter fullscreen mode Exit fullscreen mode

💡 Why This Matters

1. Compiler-Verified Exhaustiveness

No more missed cases in pattern matching! The compiler ensures you handle all union cases:

void MakeSound(Pet pet)
{
    switch (pet)
    {
        case Pet.cat var: Console.WriteLine($"{var.Name} says Meow!");
        case Pet.dog var: Console.WriteLine($"{var.Name} says Woof!");
        case Pet.bird var: Console.WriteLine($"{var.Name} chirps!");
        // No warning if we add a new union case later!
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Automatic Unwrapping

Access the underlying value directly within pattern matches:

if (pet is Pet.cat { cat: { Name: "Fluffy" } } catPet)
{
    // catPet.cat.Name is "Fluffy" - no extra dereferencing!
    Console.WriteLine($"Meet {catPet.cat.Name}!");
}
Enter fullscreen mode Exit fullscreen mode

3. Enhanced Nullability

Union types integrate with nullable reference types:

union Result<T, TError> { Success<T>, Error<TError> }

// Compiler tracks which cases allow null safely
Enter fullscreen mode Exit fullscreen mode

4. Less Boilerplate

No more marker interfaces:

// Before (20 lines of boilerplate)
public interface IResult;
public sealed class Success<T> : IResult;
public sealed class Error<T> : IResult;

// Now (2 lines)
union Result<T, TError> { Success<T>, Error<TError> }
Enter fullscreen mode Exit fullscreen mode

🔥 Real-World Use Cases

Error Handling

union ValidationFailure { 
    RequiredFieldMissing,
    InvalidEmail,
    InvalidAge,
    DuplicateEntry 
}

ValidationFailure error = ValidationFailure.InvalidEmail;

switch (error)
{
    case ValidationFailure.InvalidEmail var:
        Console.WriteLine("Please enter a valid email.");
        break;
}
Enter fullscreen mode Exit fullscreen mode

State Machines

union OrderState { 
    Pending,
    Shipped,
    Delivered,
    Cancelled 
}

OrderState state = OrderState.Pending;
Enter fullscreen mode Exit fullscreen mode

âš¡ Pro Tips

  1. Watch your value types: Default value types may get boxed in unions.
  2. Use TryGetValue() patterns to avoid performance pitfalls.
  3. Pattern matching becomes your best friend for exhaustive checks.

🎯 Future Outlook

This feature positions C# as a leading language for:

  • Algebraic Data Types (ADTs) - bringing functional programming concepts mainstream
  • Type-driven development - compile-time guarantees over runtime surprises
  • Developer productivity - less boilerplate, more safety

📚 Resources


👤 About: Visit https://vrbagalcnd.github.io/portfolio-site

🔗 Follow: https://linkedin.com/in/vikrant-bagal

dotnet #Csharp #UnionTypes #NET11 #CSharp15 #TypeSafety #SoftwareEngineering

Top comments (0)