
C# Unie dyskryminacyjne (Jak to działa dla programistów)
Unie dyskryminowane, znane również jako unie tagowane lub sumaryczne, są potężnym narzędziem do modelowania danych, które mogą przyjmować różne formy, ale z dobrze zdefiniowanymi i ograniczonymi możliwymi przypadkami. Chociaż C# nie posiada natywnych unii dyskryminowanych, jak niektóre inne języki (np. F# lub Rust), można symulować unie dyskryminowane przy użyciu kilku technik w tym języku. W tym poradniku zgłębimy unie dyskryminowane, jak je zaimplementować w C#, oraz ich praktyczne zastosowanie z biblioteka IronPDF.
Co to jest unia dyskryminowana?
W prostych słowach, unia dyskryminowana to typ, który może przechowywać jedną z kilku z góry określonych form lub wartości. Zapewnia sposób na stworzenie bezpiecznej pod względem typów struktury, która kapsułuje różne typy lub wartości, zapewniając jednocześnie, że tylko prawidłowe przypadki są obsługiwane na etapie kompilacji.
Wyobraź sobie scenariusz, w którym chcesz przedstawić wynik operacji. Operacja może zakończyć się sukcesem, zwracając dane, lub zakończyć się niepowodzeniem, zwracając komunikat o błędzie. Unia dyskryminowana pozwoliłaby ci przedstawić te dwa możliwe wyniki w jednym typie.
Example: Simulating Discriminated Union in C#
Oto przykład, jak można symulować unię dyskryminowaną w C# przy użyciu struktury klasowej:
// 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);
}
W tym przykładzie, OperationResult<t> jest abstrakcyjną klasą, która reprezentuje nasz typ unii rozróżnialnej. Może być albo Success z wartością typu T, albo Failure z komunikatem błędu. Prywatny konstruktor zapewnia, że instancje takiej klasy mogą być tworzone tylko poprzez z góry określone przypadki.
Użycie dopasowywania wzorców z uniami dyskryminowanymi
C# zapewnia potężne możliwości dopasowywania wzorców, które dobrze współpracują z uniami dyskryminowanymi. Rozszerzmy nasz przykład OperationResult<t> o metodę, która obsługuje różne przypadki przy użyciu wyrażenia 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.
'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")
' };Wyrażenie switch tutaj obsługuje zarówno przypadki Success jak i Failure klasy OperationResult<int>. To zapewnia, że wszystkie możliwe przypadki są obsługiwane na etapie kompilacji, zapewniając bezpieczeństwo typów i zmniejszając ryzyko błędów w czasie wykonania.
Metody rozszerzające dla unii dyskryminowanych
Możesz rozszerzyć funkcjonalność unii dyskryminowanych przy użyciu metod rozszerzających. Na przykład, stwórzmy metodę rozszerzoną dla naszego OperationResult<t>, aby określić, czy wynik jest sukcesem:
// 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 ModuleTa metoda statyczna sprawdza, czy wynik jest instancją przypadku Success.
Native Support for Discriminated Unions in C#
C# nie posiada natywnego wsparcia dla unii dyskryminowanych, jak niektóre inne języki, ale w społeczności trwają dyskusje na temat dodania takiej funkcji. Natywne unie dyskryminowane ułatwiłyby definiowanie i pracę z typami unii bez konieczności polegania na hierarchiach klas.
Błędy kompilatora i bezpieczeństwo typów
Jedną z kluczowych zalet unii dyskryminowanych jest bezpieczeństwo typów, które zapewniają. Ponieważ wszystkie możliwe przypadki są znane na etapie kompilacji, kompilator może wymusić, że wszystkie przypadki są obsługiwane. To prowadzi do mniejszej liczby błędów w czasie wykonania i czyni kod mniej podatnym na błędy.
Na przykład, jeśli zapomnisz obsłużyć konkretny przypadek w instrukcji switch, kompilator wygeneruje błąd, nakłaniając cię do obsługi brakującego przypadku. Jest to szczególnie przydatne przy pracy ze złożonymi strukturami danych z wieloma możliwymi przypadkami.
Using IronPDF with Discriminated Unions in C#

IronPDF to biblioteka PDF dla C#, która pomaga programistom tworzyć pliki PDF z HTML i umożliwia im modyfikowanie plików PDF bez problemu. Podczas pracy z plikami PDF w C#, możesz zintegrować IronPDF z uniami dyskryminowanymi, aby obsłużyć różne scenariusze przy generowaniu lub przetwarzaniu plików PDF. Na przykład, możesz mieć proces, który albo z powodzeniem generuje plik PDF, albo napotyka błąd. Unie dyskryminowane pozwalają jasno modelować ten proces. Stwórzmy prosty przykład, w którym generujemy plik PDF przy użyciu IronPDF i zwracamy wynik jako unię dyskryminowaną.
// 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(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(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(pdf As PdfDocument) As PdfResult
Return New Success(pdf)
End Function
' Factory method to create a failed PDF result.
Public Shared Function CreateFailure(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(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 ClassKlasa PdfResult reprezentuje unię rozróżnialną z dwoma przypadkami: Success i Failure. Przypadek Success zawiera PdfDocument, podczas gdy przypadek Failure przechowuje komunikat błędu. Metoda GeneratePdf przyjmuje ciąg HTML, próbuje wygenerować PDF przy użyciu IronPDF i zwraca wynik jako PdfResult. Jeśli generowanie PDF się powiedzie, zwraca przypadek Success z wygenerowanym PDF. Jeśli wystąpi wyjątek, zwraca przypadek Failure z komunikatem błędu.
Wnioski

Unie dyskryminowane w C# oferują potężny i elastyczny sposób na modelowanie danych z wieloma możliwymi przypadkami. Chociaż C# nie obsługuje unii dyskryminowanych, możesz je symulować przy użyciu hierarchii klas, dopasowywania wzorców i innych technik. Efekt jest bardziej bezpieczny pod względem typów, mniej podatny na błędy i łatwiejszy w utrzymaniu.
IronPDF oferuje bezpłatną wersję próbną, aby umożliwić ci poznanie oprogramowania bez żadnych kosztów na wstępie. Możesz odkrywać wszystkie funkcje i zobaczyć, jak wpisują się w twoje potrzeby. Po okresie próbnym licencje są dostępne od $999.

Jacob Mellor jest Chief Technology Officer w Iron Software i wizjonerskim inżynierem, pionierem technologii C# PDF. Jako pierwotny deweloper głównej bazy kodowej Iron Software, kształtuje architekturę produktów firmy od jej początku, przekształcając ją wspólnie z CEO Cameron Rimington w firmę liczącą ponad 50 osób, obsługującą NASA, Teslę i światowe agencje rządowe.
Powiązane artykuły


