Después de que la comunidad de C# pidiera durante muchos años los discriminated unions en el lenguaje de forma nativa, parece que por fin tenemos algo utilizable, aunque está en versión preview, hoy vamos a ver por qué está causando tanto alboroto en la comunidad.
Tabla de contenidos
NOTA: este post está escrito a finales de Abril del 2026 y esto es importante a tener en cuenta por si algo cambia con respecto a la versión final, en cuyo caso, tendrás un enlace.
1 - Qué son los Union Types?
Un Union Type (conocido en lenguajes funcionales como Discriminated Union) es un tipo de dato que puede almacenar exactamente uno de varios tipos posibles. A diferencia de una clase tradicional que tiene múltiples propiedades, una unión define estados exclusivos: o eres una cosa, o eres la otra, pero nunca ambas simultáneamente.
Para poner un ejemplo, muchos estaréis familiarizados con el patrón Result, donde el objeto Result puede ser de tipo T o 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;
}
}
El equivalente con union types sería lo siguiente:
public record Success<T>(T Value);
public record Failure(string Message);
public union Result<T>(Success<T>, Failure);
Este patrón funciona muy bien en combinación con el pattern matching por ejemplo el result anterior podemos tener los siguiente
Result<int> result = new Success<int>(42);
string test = result switch
{
Success<int> s => $"Value: {s.Value}",
Failure f => $"Error: {f.Message}",
};
Al ser utilizado dentro de pattern matching, si añadieras cualquier otro objeto al union, el compilador te daría error, cosa que no pasa directamente con las soluciones actuales.
2 - Qué ventajas nos trae type unions frente a las opciones actuales?
Hay dos situaciones principales donde los type unions tienen un efecto directo:
La primera es para el reemplazo de las librerías tipo Result o OneOf que muchos hemos estado utilizando durante años como base de nuestras aplicaciones.
Con esta funcionalidad del lenguaje obtenemos soporte nativo, lo que siempre va bien porque poco a poco se van a ir añadiendo configuraciones con otras partes del lenguaje para que trabajen en sincronía, como pasa con el pattern matching.
Al final siempre es mejor que esté todo dentro del lenguaje en vez de en librerías de terceros.
El segundo punto donde en mi opinión van a tener efecto es en el caso de las tuplas; Si bien es cierto que resuelven problemas distintos, una tupla funciona (o debería) como un “and” y el union type como “or” para ser utilizado así:
(int code, string message) result = (200, "OK");
union ApiResponse(SuccessPayload, ErrorDetail);
La realidad es que en muchos escenarios la tupla se utilizaba para representar uno u otro:
(bool success, string? error, Data? data)
Utilizando campos nullables y al final complicando el código. Con los union types esto ya no será necesario!
3 - Boxing en los union types de C#
Hace años cuando desconocía la existencia de los union types o discriminated unions me puse a investigar cuál sería la diferencia real conforme a lo que ya tenemos librerías que funcionan de una forma similar a los union types. La respuesta es boxing, con union Types no tenemos boxing, lo que mejora el rendimiento.
Pero, por lo menos en la primera versión, vamos a tener boxing en algunos escenarios. Si un value type se almacena en un union que contiene reference types, tendríamos boxing.
Pero si todos los casos son reference types, no hay boxing. Solo se almacena una referencia.
La idea es que durante los meses venideros hasta que el equipo de C# lance la implementación final van a estar mejorando el rendimiento y como el runtime trabaja con ellos.
En mi opinión, para la gran mayoría de casos, no debería de preocuparnos mucho el tema del boxing, sobre todo si estás trabajando en dominio. Si tu código está en un hotpath que procesa millones de operaciones por segundo, vigila como construyes el union type ya que si mezclas structs con clases, ese boxing extra podría afectar al rendimiento.