AYUDA .NET

Unión discriminada en C# (Cómo funciona para desarrolladores)

Kannaopat Udonpant
Kannapat Udonpant
23 de octubre, 2024
Compartir:

Sindicatos discriminados, también conocidas como uniones etiquetadas o tipos suma, representan una potente herramienta para modelar datos que pueden adoptar diferentes formas, pero con casos posibles bien definidos y limitados. Aunque C# no tiene uniones nativas discriminadas como otros lenguajes(por ejemplo, F# o Rust), puede simular uniones discriminadas utilizando varias técnicas del lenguaje. En este tutorial, nos adentraremos en las uniones discriminadas, cómo implementarlas en C# y su caso de uso práctico con la aplicaciónBiblioteca IronPDF.

¿Qué es una unión discriminada?

En términos sencillos, una unión discriminada es un tipo que puede contener una de varias formas o valores predefinidos. Proporciona una forma de crear una estructura a prueba de tipos que encapsula diferentes tipos o valores al tiempo que garantiza en tiempo de compilación que solo se tratan los casos válidos.

Imagínese una situación en la que desea representar el resultado de una operación. La operación puede tener éxito, devolviendo algunos datos, o fallar, devolviendo un mensaje de error. Una unión discriminada le permitiría representar estos dos posibles resultados en un solo tipo.

Ejemplo: Simulación de Unión Discriminada en C#

He aquí un ejemplo de cómo se puede simular una unión discriminada en C# utilizando una estructura de clases:

public abstract class OperationResult<T>
{
    private OperationResult() { }
    public sealed class Success : OperationResult<T>
    {
        public T Value { get; }
        public Success(T value) => Value = value;
        public override string ToString() => $"Success: {Value}";
    }
    public sealed class Failure : OperationResult<T>
    {
        public string Error { get; }
        public Failure(string error) => Error = error;
        public override string ToString() => $"Failure: {Error}";
    }
    public static OperationResult<T> CreateSuccess(T value) => new Success(value);
    public static OperationResult<T> CreateFailure(string error) => new Failure(error);
}
public abstract class OperationResult<T>
{
    private OperationResult() { }
    public sealed class Success : OperationResult<T>
    {
        public T Value { get; }
        public Success(T value) => Value = value;
        public override string ToString() => $"Success: {Value}";
    }
    public sealed class Failure : OperationResult<T>
    {
        public string Error { get; }
        public Failure(string error) => Error = error;
        public override string ToString() => $"Failure: {Error}";
    }
    public static OperationResult<T> CreateSuccess(T value) => new Success(value);
    public static OperationResult<T> CreateFailure(string error) => new Failure(error);
}

En este ejemplo, OperationResultes una clase abstracta que representa nuestro tipo de unión discriminada. Puede ser un Éxito con un valor de tipo T o un Fallo con un mensaje de error. El constructor privado garantiza que las instancias de dicha clase sólo puedan crearse a través de los casos predefinidos.

Uso de la concordancia de patrones con uniones discriminadas

C# ofrece potentes funciones de concordancia de patrones que funcionan bien con uniones discriminadas. Ampliemos nuestra OperationResultejemplo con un método que maneja diferentes casos utilizando una expresión switch.

public string HandleResult(OperationResult<int> result) =>
    result switch
    {
        OperationResult<int>.Success success => $"Operation succeeded with value: {success.Value}",
        OperationResult<int>.Failure failure => $"Operation failed with error: {failure.Error}",
        _ => throw new InvalidOperationException("Unexpected result type")
    };
public string HandleResult(OperationResult<int> result) =>
    result switch
    {
        OperationResult<int>.Success success => $"Operation succeeded with value: {success.Value}",
        OperationResult<int>.Failure failure => $"Operation failed with error: {failure.Error}",
        _ => throw new InvalidOperationException("Unexpected result type")
    };

La expresión de conmutación aquí maneja tanto los casos de éxito como de fracaso de OperationResult. Esto garantiza que se cubran todos los casos posibles en tiempo de compilación, proporcionando seguridad de tipo y reduciendo el riesgo de errores en tiempo de ejecución.

Métodos de extensión para uniones discriminadas

Puede ampliar la funcionalidad de las uniones discriminadas utilizando métodos de extensión. Por ejemplo, creemos un método de extensión para nuestro OperationResultpara determinar si el resultado es un éxito:

public static class OperationResultExtensions
{
    public static bool IsSuccess<T>(this OperationResult<T> result) =>
        result is OperationResult<T>.Success;
}
public static class OperationResultExtensions
{
    public static bool IsSuccess<T>(this OperationResult<T> result) =>
        result is OperationResult<T>.Success;
}

Este método bool estático público comprueba si el resultado es una instancia del caso Éxito.

Soporte nativo para uniones discriminadas en C#

C# no tiene soporte nativo para uniones discriminadas como otros lenguajes, pero hay discusiones en curso en la comunidad sobre la adición de tal característica. Las uniones discriminadas nativas facilitarían la definición y el trabajo con tipos de unión sin necesidad de depender de jerarquías de clases.

Errores del compilador y seguridad de tipos

Una de las principales ventajas de las uniones discriminadas es el tipo de seguridad que proporcionan. Dado que se conocen todos los casos posibles en tiempo de compilación, el compilador puede exigir que se traten todos los casos. De este modo se reducen los errores en tiempo de ejecución y el código es menos propenso a errores.

Por ejemplo, si se olvida de tratar un caso específico en una sentencia switch, el compilador producirá un error y le pedirá que trate el caso que falta. Esto es especialmente útil cuando se trata de estructuras de datos complejas con múltiples casos posibles.

Uso de IronPDF con uniones discriminadas en C#

Unión discriminada en C#(Cómo funciona para los desarrolladores): Figura 1 - IronPDF

IronPDF es una biblioteca PDF en C# que ayuda a los desarrolladores acrear archivos PDF a partir de HTML y les permite modificar archivos PDF sin complicaciones. Al trabajar con archivos PDF en C#, puede integrar IronPDF con uniones discriminadas para manejar diferentes escenarios al generar o procesar archivos PDF. Por ejemplo, puede haber un proceso que genere un PDF con éxito o que se encuentre con un error. Las uniones discriminadas permiten modelar este proceso con claridad. Creemos un ejemplo sencillo en el que generamos un PDF utilizando IronPDF y devolvemos el resultado como una unión discriminada.

using IronPdf;
using System;
public abstract class PdfResult
{
    private PdfResult() { }
    public sealed class Success : PdfResult
    {
        public PdfDocument Pdf { get; }
        public Success(PdfDocument pdf) => Pdf = pdf;
        public override string ToString() => "PDF generation succeeded";
    }
    public sealed class Failure : PdfResult
    {
        public string ErrorMessage { get; }
        public Failure(string errorMessage) => ErrorMessage = errorMessage;
        public override string ToString() => $"PDF generation failed: {ErrorMessage}";
    }
    public static PdfResult CreateSuccess(PdfDocument pdf) => new Success(pdf);
    public static PdfResult CreateFailure(string errorMessage) => new Failure(errorMessage);
}
public class PdfGenerator
{
    public PdfResult GeneratePdf(string htmlContent)
    {
        try
        {
            var renderer = new ChromePdfRenderer();
            var pdf = renderer.RenderHtmlAsPdf(htmlContent);
            return PdfResult.CreateSuccess(pdf);
        }
        catch (Exception ex)
        {
            return PdfResult.CreateFailure(ex.Message);
        }
    }
}
using IronPdf;
using System;
public abstract class PdfResult
{
    private PdfResult() { }
    public sealed class Success : PdfResult
    {
        public PdfDocument Pdf { get; }
        public Success(PdfDocument pdf) => Pdf = pdf;
        public override string ToString() => "PDF generation succeeded";
    }
    public sealed class Failure : PdfResult
    {
        public string ErrorMessage { get; }
        public Failure(string errorMessage) => ErrorMessage = errorMessage;
        public override string ToString() => $"PDF generation failed: {ErrorMessage}";
    }
    public static PdfResult CreateSuccess(PdfDocument pdf) => new Success(pdf);
    public static PdfResult CreateFailure(string errorMessage) => new Failure(errorMessage);
}
public class PdfGenerator
{
    public PdfResult GeneratePdf(string htmlContent)
    {
        try
        {
            var renderer = new ChromePdfRenderer();
            var pdf = renderer.RenderHtmlAsPdf(htmlContent);
            return PdfResult.CreateSuccess(pdf);
        }
        catch (Exception ex)
        {
            return PdfResult.CreateFailure(ex.Message);
        }
    }
}

La clase PdfResult representa una unión discriminada con dos casos: Éxito y Fallo. El caso de Éxito contiene un PdfDocument, mientras que el caso de Fallo contiene un mensaje de error. El método GeneratePdf toma una cadena HTML, intenta generar un PDF utilizando IronPDF y devuelve el resultado como PdfResult. Si la generación del PDF tiene éxito, devuelve el caso Success con el PDF generado. Si se produce una excepción, devuelve el caso de fallo con el mensaje de error.

Conclusión

Unión discriminada en C#(Cómo funciona para los desarrolladores): Figura 2 - Licencias

Las uniones discriminadas en C# proporcionan una forma potente y flexible de modelar datos con múltiples casos posibles. Aunque C# no admite uniones discriminadas, puede simularlas utilizando jerarquías de clases, concordancia de patrones y otras técnicas. El código resultante es más seguro, menos propenso a errores y más fácil de mantener.

IronPDF proporciona unprueba gratuita para ayudarle a hacerse una idea del software sin ningún coste inicial. Puedes explorar todas las características y ver cómo se alinean con tus necesidades. Después de su prueba, las licencias están disponibles desde $749.

Kannaopat Udonpant
Ingeniero de software
Antes de convertirse en ingeniero de software, Kannapat realizó un doctorado en Recursos Medioambientales en la Universidad de Hokkaido (Japón). Mientras cursaba su licenciatura, Kannapat también se convirtió en miembro del Laboratorio de Robótica Vehicular, que forma parte del Departamento de Ingeniería de Bioproducción. En 2022, aprovechó sus conocimientos de C# para unirse al equipo de ingeniería de Iron Software, donde se centra en IronPDF. Kannapat valora su trabajo porque aprende directamente del desarrollador que escribe la mayor parte del código utilizado en IronPDF. Además del aprendizaje entre iguales, Kannapat disfruta del aspecto social de trabajar en Iron Software. Cuando no está escribiendo código o documentación, Kannapat suele jugar con su PS5 o volver a ver The Last of Us.
< ANTERIOR
C# HttpClient (Cómo funciona para desarrolladores)
SIGUIENTE >
Nuevo GUID de C# (Cómo funciona para desarrolladores)