Introduction
Handling multiple possible outcomes is a common requirement in modern applications. For example, an API might return a successful result, a validation error, or a "not found" response. Traditionally, C# developers have handled these scenarios using inheritance, exceptions, tuples, or custom result classes.
With the introduction of Union Types in .NET 11, C# provides a cleaner and more expressive way to represent values that can be one of several different types. This feature helps developers write more readable, maintainable, and type-safe code.
In this article, we'll explore what Union Types are, why they are useful, and when you should consider using them in your applications.
What Are Union Types?
A Union Type allows a variable or return value to represent one of multiple predefined types.
Instead of creating several wrapper classes or relying on exceptions for normal application flow, a Union Type clearly defines all possible outcomes.
Think of it as saying:
"This method can return either a successful result or an error."
This makes your code easier to understand because every possible outcome is explicitly defined.
Why Are Union Types Useful?
Before Union Types, developers often used approaches like:
Returning
nullThrowing exceptions
Creating multiple derived classes
Returning
objectUsing tuples
While these methods work, they can make the code harder to maintain and understand.
Union Types improve this by:
Making return values more predictable
Improving type safety
Reducing unnecessary exception handling
Making APIs easier to consume
Clearly documenting possible outcomes
A Simple Example
Imagine you're searching for a user in a database.
Without Union Types, you might write:
public User? GetUser(int id)
{
// Fetch user
}
The caller now has to determine whether a null value means the user doesn't exist or something else went wrong.
With Union Types, the method can explicitly describe every possible result.
For example:
public UserResult GetUser(int id)
{
// Returns either:
// - User
// - NotFound
}
Now the intent is much clearer.
Practical API Scenario
Suppose you're building an ASP.NET Core API.
When a client requests an order, there are several possible outcomes:
Order found
Order not found
Invalid request
Permission denied
Instead of relying on exceptions or multiple if statements, a Union Type can represent these outcomes in a single, well-defined return type.
This makes both the API implementation and the client code easier to follow.
Benefits of Union Types
Better Readability
Developers can quickly understand what a method returns without reading its implementation.
Instead of guessing whether null or an exception indicates failure, the return type clearly communicates every possible outcome.
Improved Type Safety
Because the compiler knows every supported type, it can help detect mistakes before your application runs.
This reduces runtime errors and improves code quality.
Cleaner Error Handling
Exceptions should generally represent unexpected situations.
Validation failures or missing records are often expected outcomes and can be represented more naturally using Union Types.
Easier Maintenance
As applications grow, methods often gain additional return scenarios.
Union Types make it easier to extend functionality without introducing complicated inheritance hierarchies or multiple wrapper classes.
Comparing Traditional Approaches
| Traditional Approach | Limitation | Union Types Advantage |
|---|---|---|
| Returning null | Easy to forget null checks | Explicit result types |
| Exceptions | Can affect performance for expected outcomes | Clear success and failure paths |
| object return type | No compile-time safety | Strongly typed results |
| Custom wrapper classes | More boilerplate code | Simpler and easier to understand |
Real-World Use Cases
Union Types work especially well in situations like:
REST APIs
Minimal APIs
Authentication systems
Database operations
File processing
Payment workflows
Validation logic
Background services
For example, a payment process might return:
Payment successful
Payment declined
Payment pending
Invalid payment details
Representing these outcomes with a Union Type makes the code much more expressive than relying on exceptions or status codes alone.
Best Practices
When using Union Types, keep these recommendations in mind:
Use them for methods with multiple expected outcomes.
Avoid using exceptions for normal business logic.
Keep the number of possible result types manageable.
Use descriptive names for each result type.
Document what each outcome represents.
Handle every possible case in your application logic.
Write unit tests for all supported outcomes.
Following these practices helps keep your code clean and easy to maintain.
When Should You Use Union Types?
Union Types are a good choice when:
A method can return multiple valid outcomes.
You want to improve API readability.
You need stronger compile-time safety.
You want to reduce unnecessary exception handling.
You are designing reusable libraries or services.
They may not be necessary for very simple methods that always return a single value.
Conclusion
Union Types are a valuable addition to the C# language and the .NET ecosystem. They provide a more expressive way to represent multiple valid outcomes while improving readability, maintainability, and type safety.
Instead of relying on null values, exceptions, or complex inheritance structures, developers can clearly define every possible result a method can return. This leads to cleaner APIs, simpler business logic, and code that is easier for teams to understand and maintain.
As .NET continues to evolve, features like Union Types help developers write more robust and modern applications with less boilerplate and clearer intent.

Join the conversation! Your thoughts help the community grow.