Ir para o conteúdo do rodapé
AJUDA DO .NET

União Discriminatória em C# (Como Funciona para Desenvolvedores)

As uniões discriminadas , também conhecidas como uniões etiquetadas ou tipos de soma, representam uma ferramenta poderosa para modelar dados que podem assumir diferentes formas, mas com casos possíveis bem definidos e limitados. Embora o C# não possua uniões discriminadas nativas como algumas outras linguagens (por exemplo, F# ou Rust), você pode simular uniões discriminadas usando diversas técnicas na linguagem. Neste tutorial, vamos explorar as uniões discriminadas, como implementá-las em C# e seu uso prático com a biblioteca IronPDF .

O que é uma união discriminada?

Em termos simples, uma união discriminada é um tipo que pode assumir uma de várias formas ou valores predefinidos. Ela fornece uma maneira de criar uma estrutura com segurança de tipos que encapsula diferentes tipos ou valores, garantindo em tempo de compilação que apenas os casos válidos sejam tratados.

Imagine um cenário em que você queira representar o resultado de uma operação. A operação pode ser bem-sucedida, retornando alguns dados, ou falhar, retornando uma mensagem de erro. Uma união discriminada permitiria representar esses dois resultados possíveis em um único tipo.

Exemplo: Simulando uma União Discriminada em C

Aqui está um exemplo de como você pode simular uma união discriminada em C# usando uma estrutura de classe:

// Define an abstract base class representing the operation result.
public abstract class OperationResult<t>
{
    // Private constructor to ensure the class cannot be instantiated directly.
    private OperationResult() { }

    // Nested class representing a successful operation result.
    public sealed class Success : OperationResult<t>
    {
        public T Value { get; }

        public Success(T value) => Value = value;

        public override string ToString() => $"Success: {Value}";
    }

    // Nested class representing a failed operation result.
    public sealed class Failure : OperationResult<t>
    {
        public string Error { get; }

        public Failure(string error) => Error = error;

        public override string ToString() => $"Failure: {Error}";
    }

    // Factory method to create a successful operation result.
    public static OperationResult<t> CreateSuccess(T value) => new Success(value);

    // Factory method to create a failed operation result.
    public static OperationResult<t> CreateFailure(string error) => new Failure(error);
}
// Define an abstract base class representing the operation result.
public abstract class OperationResult<t>
{
    // Private constructor to ensure the class cannot be instantiated directly.
    private OperationResult() { }

    // Nested class representing a successful operation result.
    public sealed class Success : OperationResult<t>
    {
        public T Value { get; }

        public Success(T value) => Value = value;

        public override string ToString() => $"Success: {Value}";
    }

    // Nested class representing a failed operation result.
    public sealed class Failure : OperationResult<t>
    {
        public string Error { get; }

        public Failure(string error) => Error = error;

        public override string ToString() => $"Failure: {Error}";
    }

    // Factory method to create a successful operation result.
    public static OperationResult<t> CreateSuccess(T value) => new Success(value);

    // Factory method to create a failed operation result.
    public static OperationResult<t> CreateFailure(string error) => new Failure(error);
}
Imports System

' Define an abstract base class representing the operation result.
Public MustInherit Class OperationResult(Of T)
    ' Private constructor to ensure the class cannot be instantiated directly.
    Private Sub New()
    End Sub

    ' Nested class representing a successful operation result.
    Public NotInheritable Class Success
        Inherits OperationResult(Of T)

        Public ReadOnly Property Value As T

        Public Sub New(value As T)
            Me.Value = value
        End Sub

        Public Overrides Function ToString() As String
            Return $"Success: {Value}"
        End Function
    End Class

    ' Nested class representing a failed operation result.
    Public NotInheritable Class Failure
        Inherits OperationResult(Of T)

        Public ReadOnly Property Error As String

        Public Sub New([error] As String)
            Me.Error = [error]
        End Sub

        Public Overrides Function ToString() As String
            Return $"Failure: {Error}"
        End Function
    End Class

    ' Factory method to create a successful operation result.
    Public Shared Function CreateSuccess(value As T) As OperationResult(Of T)
        Return New Success(value)
    End Function

    ' Factory method to create a failed operation result.
    Public Shared Function CreateFailure([error] As String) As OperationResult(Of T)
        Return New Failure([error])
    End Function
End Class
$vbLabelText   $csharpLabel

Neste exemplo, OperationResult<t> é uma classe abstrata que representa nosso tipo de união discriminada. Pode ser um Success com um valor do tipo T ou um Failure com uma mensagem de erro. O construtor privado garante que instâncias dessa classe só possam ser criadas por meio dos casos predefinidos.

Utilizando a correspondência de padrões com uniões discriminadas

C# oferece recursos poderosos de correspondência de padrões que funcionam bem com uniões discriminadas. Vamos expandir nosso exemplo OperationResult<t> com um método que lida com diferentes casos usando uma expressão switch.

// Method to handle the result using pattern matching.
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")
    };
// Method to handle the result using pattern matching.
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")
    };
' Method to handle the result using pattern matching.
'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")
'	};
$vbLabelText   $csharpLabel

A expressão switch aqui lida com os casos Success e Failure do OperationResult<int>. Isso garante que todos os casos possíveis sejam cobertos em tempo de compilação, proporcionando segurança de tipos e reduzindo o risco de erros em tempo de execução.

Métodos de extensão para uniões discriminadas

Você pode estender a funcionalidade de uniões discriminadas usando métodos de extensão. Por exemplo, vamos criar um método de extensão para o nosso OperationResult<t> para determinar se o resultado é um sucesso:

// Static class to hold extension methods for OperationResult<t>.
public static class OperationResultExtensions
{
    // Extension method to check if the operation result indicates success. 
    public static bool IsSuccess<t>(this OperationResult<t> result) =>
        result is OperationResult<t>.Success;
}
// Static class to hold extension methods for OperationResult<t>.
public static class OperationResultExtensions
{
    // Extension method to check if the operation result indicates success. 
    public static bool IsSuccess<t>(this OperationResult<t> result) =>
        result is OperationResult<t>.Success;
}
' Static class to hold extension methods for OperationResult(Of T).
Public Module OperationResultExtensions

    ' Extension method to check if the operation result indicates success.
    <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
$vbLabelText   $csharpLabel

Este método estático verifica se o resultado é uma instância do caso Success.

Suporte Nativo a Uniões Discriminadas em C

C# não possui suporte nativo para uniões discriminadas como algumas outras linguagens, mas há discussões em andamento na comunidade sobre a adição de tal recurso. O suporte nativo a uniões discriminadas tornaria mais fácil definir e trabalhar com esses tipos sem precisar recorrer a hierarquias de classe.

Erros de compilação e segurança de tipos

Um dos principais benefícios das uniões discriminadas é a segurança de tipos que elas proporcionam. Como todos os casos possíveis são conhecidos em tempo de compilação, o compilador pode garantir que todos os casos sejam tratados. Isso resulta em menos erros de tempo de execução e torna o código menos propenso a erros.

Por exemplo, se você se esquecer de tratar um caso específico em uma instrução switch, o compilador produzirá um erro, solicitando que você corrija o caso ausente. Isso é especialmente útil ao lidar com estruturas de dados complexas com múltiplos casos possíveis.

Usando o IronPDF com Uniões Discriminadas em C

C# Discriminated Union (Como funciona para desenvolvedores): Figura 1 - IronPDF

IronPDF é uma biblioteca C# para PDF que ajuda desenvolvedores a criar arquivos PDF a partir de HTML e permite que eles modifiquem arquivos PDF sem complicações. Ao trabalhar com PDFs em C#, você pode integrar o IronPDF com uniões discriminadas para lidar com diferentes cenários ao gerar ou processar arquivos PDF. Por exemplo, você pode ter um processo que gera um PDF com sucesso ou que encontra um erro. As uniões discriminadas permitem que você modele esse processo de forma clara. Vamos criar um exemplo simples onde geramos um PDF usando o IronPDF e retornamos o resultado como uma união discriminada.

// Using directives for necessary namespaces.
using IronPdf;
using System;

// Define an abstract base class representing the PDF generation result.
public abstract class PdfResult
{
    // Private constructor to ensure the class cannot be instantiated directly.
    private PdfResult() { }

    // Nested class representing a successful PDF generation result.
    public sealed class Success : PdfResult
    {
        public PdfDocument Pdf { get; }

        public Success(PdfDocument pdf) => Pdf = pdf;

        public override string ToString() => "PDF generation succeeded";
    }

    // Nested class representing a failed PDF generation result.
    public sealed class Failure : PdfResult
    {
        public string ErrorMessage { get; }

        public Failure(string errorMessage) => ErrorMessage = errorMessage;

        public override string ToString() => $"PDF generation failed: {ErrorMessage}";
    }

    // Factory method to create a successful PDF result.
    public static PdfResult CreateSuccess(PdfDocument pdf) => new Success(pdf);

    // Factory method to create a failed PDF result.
    public static PdfResult CreateFailure(string errorMessage) => new Failure(errorMessage);
}

// Class to generate PDFs using IronPDF.
public class PdfGenerator
{
    // Method to generate a PDF from HTML content and return the result as a PdfResult.
    public PdfResult GeneratePdf(string htmlContent)
    {
        try
        {
            // Create a new ChromePdfRenderer instance.
            var renderer = new ChromePdfRenderer();

            // Attempt to render the HTML content as a PDF.
            var pdf = renderer.RenderHtmlAsPdf(htmlContent);

            // Return a success result with the generated PDF.
            return PdfResult.CreateSuccess(pdf);
        }
        catch (Exception ex)
        {
            // Return a failure result with the error message if an exception occurs.
            return PdfResult.CreateFailure(ex.Message);
        }
    }
}
// Using directives for necessary namespaces.
using IronPdf;
using System;

// Define an abstract base class representing the PDF generation result.
public abstract class PdfResult
{
    // Private constructor to ensure the class cannot be instantiated directly.
    private PdfResult() { }

    // Nested class representing a successful PDF generation result.
    public sealed class Success : PdfResult
    {
        public PdfDocument Pdf { get; }

        public Success(PdfDocument pdf) => Pdf = pdf;

        public override string ToString() => "PDF generation succeeded";
    }

    // Nested class representing a failed PDF generation result.
    public sealed class Failure : PdfResult
    {
        public string ErrorMessage { get; }

        public Failure(string errorMessage) => ErrorMessage = errorMessage;

        public override string ToString() => $"PDF generation failed: {ErrorMessage}";
    }

    // Factory method to create a successful PDF result.
    public static PdfResult CreateSuccess(PdfDocument pdf) => new Success(pdf);

    // Factory method to create a failed PDF result.
    public static PdfResult CreateFailure(string errorMessage) => new Failure(errorMessage);
}

// Class to generate PDFs using IronPDF.
public class PdfGenerator
{
    // Method to generate a PDF from HTML content and return the result as a PdfResult.
    public PdfResult GeneratePdf(string htmlContent)
    {
        try
        {
            // Create a new ChromePdfRenderer instance.
            var renderer = new ChromePdfRenderer();

            // Attempt to render the HTML content as a PDF.
            var pdf = renderer.RenderHtmlAsPdf(htmlContent);

            // Return a success result with the generated PDF.
            return PdfResult.CreateSuccess(pdf);
        }
        catch (Exception ex)
        {
            // Return a failure result with the error message if an exception occurs.
            return PdfResult.CreateFailure(ex.Message);
        }
    }
}
' Using directives for necessary namespaces.
Imports IronPdf
Imports System

' Define an abstract base class representing the PDF generation result.
Public MustInherit Class PdfResult
	' Private constructor to ensure the class cannot be instantiated directly.
	Private Sub New()
	End Sub

	' Nested class representing a successful PDF generation result.
	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

	' Nested class representing a failed PDF generation result.
	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

	' Factory method to create a successful PDF result.
	Public Shared Function CreateSuccess(ByVal pdf As PdfDocument) As PdfResult
		Return New Success(pdf)
	End Function

	' Factory method to create a failed PDF result.
	Public Shared Function CreateFailure(ByVal errorMessage As String) As PdfResult
		Return New Failure(errorMessage)
	End Function
End Class

' Class to generate PDFs using IronPDF.
Public Class PdfGenerator
	' Method to generate a PDF from HTML content and return the result as a PdfResult.
	Public Function GeneratePdf(ByVal htmlContent As String) As PdfResult
		Try
			' Create a new ChromePdfRenderer instance.
			Dim renderer = New ChromePdfRenderer()

			' Attempt to render the HTML content as a PDF.
			Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)

			' Return a success result with the generated PDF.
			Return PdfResult.CreateSuccess(pdf)
		Catch ex As Exception
			' Return a failure result with the error message if an exception occurs.
			Return PdfResult.CreateFailure(ex.Message)
		End Try
	End Function
End Class
$vbLabelText   $csharpLabel

A classe PdfResult representa uma união discriminada com dois casos: Success e Failure. O caso Success contém um PdfDocument, enquanto o caso Failure contém uma mensagem de erro. O método GeneratePdf recebe uma string HTML, tenta gerar um PDF usando o IronPDF e retorna o resultado como um PdfResult. Se a geração do PDF for bem-sucedida, ela retorna o caso Success com o PDF gerado. Se ocorrer uma exceção, retorna o caso Failure com a mensagem de erro.

Conclusão

C# Discriminated Union (Como funciona para desenvolvedores): Figura 2 - Licenciamento

Em C#, as uniões discriminadas oferecem uma maneira poderosa e flexível de modelar dados com múltiplos casos possíveis. Embora o C# não suporte uniões discriminadas, você pode simulá-las usando hierarquias de classes, correspondência de padrões e outras técnicas. O código resultante é mais seguro em relação aos tipos, menos propenso a erros e mais fácil de manter.

A IronPDF oferece um período de teste gratuito para que você possa experimentar o software sem custos iniciais. Você pode explorar todas as funcionalidades e ver como elas se alinham às suas necessidades. Após o período de teste, as licenças estarão disponíveis a partir de $799.

Perguntas frequentes

Como posso criar uma união discriminada em C#?

Em C#, você pode criar uma união discriminada definindo uma classe abstrata com subclasses aninhadas. Cada subclasse representa um caso possível, como um estado de sucesso ou de erro, e você pode usar o casamento de padrões para lidar com esses casos.

Qual é o papel da biblioteca IronPDF no tratamento de uniões discriminadas?

A biblioteca IronPDF pode ser usada em conjunto com uniões discriminadas para gerenciar os resultados da geração de PDFs. Ao modelar esses resultados como uniões discriminadas, você garante a segurança de tipos e lida tanto com a criação bem-sucedida de PDFs quanto com quaisquer erros que ocorram.

Como o casamento de padrões aprimora as uniões discriminadas em C#?

O casamento de padrões aprimora as uniões discriminadas em C#, permitindo que os desenvolvedores lidem elegantemente com cada caso possível. Com o casamento de padrões, você pode gerenciar com segurança diferentes resultados, garantindo que todos os cenários sejam cobertos em tempo de compilação.

Por que as uniões discriminadas são benéficas para a geração de PDFs em C#?

As uniões discriminadas são benéficas para a geração de PDFs em C# porque fornecem uma maneira estruturada de lidar com casos de sucesso e erro. Essa abordagem garante que os problemas potenciais sejam resolvidos em tempo de compilação, reduzindo erros de tempo de execução durante a criação do PDF.

É possível estender as uniões discriminadas para adicionar funcionalidades extras em C#?

Sim, uniões discriminadas podem ser estendidas com funcionalidades adicionais usando métodos de extensão. Isso permite adicionar comportamentos personalizados, como verificar o status de sucesso da geração de um PDF, sem alterar a estrutura base.

Existe alguma maneira de simular uniões discriminadas em C# sem suporte nativo?

Sim, embora o C# não tenha suporte nativo para uniões discriminadas, elas podem ser simuladas usando hierarquias de classes. Uma classe base abstrata pode ser usada com classes aninhadas para representar diferentes resultados possíveis, como casos de sucesso ou falha.

Como os desenvolvedores de C# podem lidar com erros na geração de PDFs de forma eficaz?

Os desenvolvedores C# podem lidar com erros na geração de PDFs de forma eficaz usando uniões discriminadas para modelar os resultados potenciais. Essa abordagem garante que os erros sejam tratados em tempo de compilação, melhorando a confiabilidade e a capacidade de manutenção do código.

Quais são as vantagens de usar o IronPDF com uniões discriminadas em projetos C#?

Utilizar o IronPDF com uniões discriminadas em projetos C# oferece a vantagem de um tratamento de erros robusto durante a geração de PDFs. Essa combinação permite uma distinção clara entre operações bem-sucedidas e erros, aumentando a segurança e a confiabilidade do código.

Como as uniões discriminadas contribuem para a segurança de tipos em C#?

As uniões discriminadas contribuem para a segurança de tipos em C# ao garantir que todos os casos potenciais sejam tratados durante a compilação. Isso reduz a probabilidade de erros em tempo de execução e torna o código mais previsível e fácil de manter.

Curtis Chau
Redator Técnico

Curtis Chau é bacharel em Ciência da Computação (Universidade Carleton) e se especializa em desenvolvimento front-end, com experiência em Node.js, TypeScript, JavaScript e React. Apaixonado por criar interfaces de usuário intuitivas e esteticamente agradáveis, Curtis gosta de trabalhar com frameworks modernos e criar manuais ...

Leia mais

Equipe de suporte de ferro

Estamos online 24 horas por dia, 5 dias por semana.
Bater papo
E-mail
Liga para mim