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!
💡 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!
}
}
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}!");
}
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
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> }
🔥 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;
}
State Machines
union OrderState {
Pending,
Shipped,
Delivered,
Cancelled
}
OrderState state = OrderState.Pending;
âš¡ Pro Tips
- Watch your value types: Default value types may get boxed in unions.
-
Use
TryGetValue()patterns to avoid performance pitfalls. - 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
Top comments (0)