AYUDA .NET

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

Publicado en 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);
}
Public MustInherit Class OperationResult(Of T)
	Private Sub New()
	End Sub
	Public NotInheritable Class Success
		Inherits OperationResult(Of T)

		Public ReadOnly Property Value() As T
		Public Sub New(ByVal value As T)
			Me.Value = value
		End Sub
		Public Overrides Function ToString() As String
			Return $"Success: {Value}"
		End Function
	End Class
	Public NotInheritable Class Failure
		Inherits OperationResult(Of T)

		Public ReadOnly Property [Error]() As String
		Public Sub New(ByVal [error] As String)
			Me.Error = [error]
		End Sub
		Public Overrides Function ToString() As String
			Return $"Failure: {[Error]}"
		End Function
	End Class
	Public Shared Function CreateSuccess(ByVal value As T) As OperationResult(Of T)
		Return New Success(value)
	End Function
	Public Shared Function CreateFailure(ByVal [error] As String) As OperationResult(Of T)
		Return New Failure([error])
	End Function
End Class
VB   C#

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")
    };
'INSTANT VB TODO TASK: The following 'switch expression' was not converted by Instant VB:
'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")
'	};
VB   C#

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;
}
Public Module OperationResultExtensions
	<System.Runtime.CompilerServices.Extension> _
	Public Function IsSuccess(Of T)(ByVal result As OperationResult(Of T)) As Boolean
		Return TypeOf result Is OperationResult(Of T).Success
	End Function
End Module
VB   C#

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);
        }
    }
}
Imports IronPdf
Imports System
Public MustInherit Class PdfResult
	Private Sub New()
	End Sub
	Public NotInheritable Class Success
		Inherits PdfResult

		Public ReadOnly Property Pdf() As PdfDocument
		Public Sub New(ByVal pdf As PdfDocument)
			Me.Pdf = pdf
		End Sub
		Public Overrides Function ToString() As String
			Return "PDF generation succeeded"
		End Function
	End Class
	Public NotInheritable Class Failure
		Inherits PdfResult

		Public ReadOnly Property ErrorMessage() As String
		Public Sub New(ByVal errorMessage As String)
			Me.ErrorMessage = errorMessage
		End Sub
		Public Overrides Function ToString() As String
			Return $"PDF generation failed: {ErrorMessage}"
		End Function
	End Class
	Public Shared Function CreateSuccess(ByVal pdf As PdfDocument) As PdfResult
		Return New Success(pdf)
	End Function
	Public Shared Function CreateFailure(ByVal errorMessage As String) As PdfResult
		Return New Failure(errorMessage)
	End Function
End Class
Public Class PdfGenerator
	Public Function GeneratePdf(ByVal htmlContent As String) As PdfResult
		Try
			Dim renderer = New ChromePdfRenderer()
			Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
			Return PdfResult.CreateSuccess(pdf)
		Catch ex As Exception
			Return PdfResult.CreateFailure(ex.Message)
		End Try
	End Function
End Class
VB   C#

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.

< ANTERIOR
C# HttpClient (Cómo funciona para desarrolladores)
SIGUIENTE >
Nuevo GUID de C# (Cómo funciona para desarrolladores)

¿Listo para empezar? Versión: 2024.12 acaba de salir

Descarga gratuita de NuGet Descargas totales: 11,810,873 Ver licencias >