After many years of the C# community asking for discriminated unions in the language natively, it looks like we finally have something usable, even if it is still in preview. Today we are going to see why it is causing so much buzz in the community.
Table of contents
NOTE: this post was written at the end of April 2026, and this is important to keep in mind in case anything changes in the final version, in which case you will have a link.
1 - What are Union Types?
A Union Type, known in functional languages as a Discriminated Union, is a data type that can store exactly one of several possible types. Unlike a traditional class that has multiple properties, a union defines exclusive states: you are either one thing or the other, but never both at the same time.
To give an example, many of you will be familiar with the Result pattern, where the Result object can be of type T or Error.
public struct Result<T>
{
public readonly T Value;
public static implicit operator Result<T>(T value) => new Result<T>(value);
public static implicit operator Result<T>(ImmutableArray<Error> errors) => new Result<T>(errors);
public readonly ImmutableArray<Error> Errors;
public bool Success => Errors.Length == 0;
public Result(T value)
{
Value = value;
Errors = ImmutableArray<Error>.Empty;
}
public Result(ImmutableArray<Error> errors)
{
if (errors.Length == 0)
{
throw new InvalidOperationException("You should specify at least one error");
}
Value = default(T);
Errors = errors;
}
}
The equivalent with union types would be the following:
public record Success<T>(T Value);
public record Failure(string Message);
public union Result<T>(Success<T>, Failure);
This pattern works very well in combination with pattern matching. For example, with the previous result we can have the following:
Result<int> result = new Success<int>(42);
string test = result switch
{
Success<int> s => $"Value: {s.Value}",
Failure f => $"Error: {f.Message}",
};When used inside pattern matching, if you added any other object to the union, the compiler would give you an error, which is not something that happens directly with the current solutions.
2 - What advantages do type unions bring compared to the current options?
There are two main situations where type unions have a direct effect:
The first is as a replacement for Result-style or OneOf libraries that many of us have been using for years as the foundation of our applications.
With this language feature we get native support, which is always good because little by little integrations will be added with other parts of the language so they can work in sync, as happens with pattern matching.
In the end, it is always better to have everything inside the language instead of in third-party libraries.
The second point where, in my opinion, they will have an effect is in the case of tuples. While it is true that they solve different problems, a tuple works, or should work, like an “and” and the union type like an “or”, to be used like this:
(int code, string message) result = (200, "OK");union ApiResponse(SuccessPayload, ErrorDetail);The reality is that in many scenarios the tuple was used to represent one or the other:
(bool success, string? error, Data? data)
Using nullable fields and ultimately making the code more complicated. With union types, this will no longer be necessary!
3 - Boxing in C# union types
Years ago, when I did not know about the existence of union types or discriminated unions, I started investigating what the real difference would be compared to what we already have, since there are libraries that work in a similar way to union types. The answer is boxing. With union types we do not have boxing, which improves performance.
But, at least in the first version, we are going to have boxing in some scenarios. If a value type is stored in a union that contains reference types, we would have boxing.
But if all cases are reference types, there is no boxing. Only one reference is stored.
The idea is that during the coming months, until the C# team releases the final implementation, they will be improving performance and how the runtime works with them.
In my opinion, for the vast majority of cases, the boxing topic should not worry us too much, especially if you are working in the domain layer. If your code is in a hot path that processes millions of operations per second, watch how you build the union type, because if you mix structs with classes, that extra boxing could affect performance.