C# 区别联合(开发者用法)
可辨识联合,也称为标记联合或和类型,是一种强大的工具,用于建模可以采取不同形式但具有明确且有限可能情况的数据。 虽然C#不像其他一些语言(例如F#或Rust)那样具有原生的可辨识联合,但您可以使用语言中的几种技术模拟可辨识联合。 在本教程中,我们将深入研究可辨识联合,如何在C#中实现它们,并通过IronPDF库探讨其实用用例。
什么是可辨识联合?
简而言之,可辨识联合是一种可以包含几种预定义形式或值的类型。 它提供了一种创建类型安全结构的方法,能够封装不同类型或值,同时在编译时确保只有有效的情况被处理。
想象一个您希望表示操作结果的场景。 操作可能成功,返回一些数据,或者失败,返回一条错误消息。 可辨识联合可以让您在单一类型中表示这两种可能的结果。
Example: Simulating Discriminated Union in C#
以下是如何使用类结构在C#中模拟可辨识联合的示例:
// 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 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)
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' 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)
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在此示例中,OperationResult<t> 是一个抽象类,表示我们的判别联合类型。 它可以是一个带有类型为 T 的值的 Success 或包含错误消息的 Failure。 私有构造函数确保这种类的实例只能通过预定义情况下创建。
使用模式匹配与可辨识联合
C#提供了强大的模式匹配功能,非常适合与可辨识联合一起使用。 让我们扩展我们的 OperationResult<t> 示例,添加一个使用 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")
' };' 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")
' };这里的 switch 表达式处理了 Success 和 Failure 这两种情况的 OperationResult<int>。 这确保在编译时覆盖所有可能情况,提供类型安全性并减少运行时错误的风险。
可辨识联合的扩展方法
您可以使用扩展方法扩展可辨识联合的功能。 例如,创建我们的 OperationResult<t> 的扩展方法,以确定结果是否成功:
// 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' 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这个静态方法检查结果是否为 Success 情况的实例。
Native Support for Discriminated Unions in C#
C#不像其他一些语言那样对可辨识联合提供原生支持,但社区中正在进行关于添加该功能的讨论。 原生可辨识联合将更易于定义和使用联合类型,而无需依赖类层次结构。
编译器错误和类型安全
可辨识联合的一个主要优点是它们提供的类型安全性。由于所有可能的情况在编译时已知,编译器可以确保所有情况都得到处理。 这导致更少的运行时错误,使代码更不容易出错。
例如,如果您忘记在switch语句中处理某个特定情况,编译器会产生一个错误,提示您处理缺失的情况。 这在处理具有多种可能情况的复杂数据结构时尤其有用。
Using IronPDF with Discriminated Unions in C#

IronPDF是一个C# PDF库,帮助开发人员从HTML创建PDF文件,并允许他们轻松修改PDF文件。 在C#中处理PDF时,您可以将IronPDF与可辨识联合结合使用,以处理在生成或处理PDF文件时的不同场景。 例如,您可能有一个成功生成PDF或遇到错误的过程。 可辨识联合允许您清晰地建模此过程。 让我们创建一个简单的示例,使用IronPDF生成PDF并将结果作为可辨识联合返回。
// 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(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 Class' 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 ClassPdfResult 类表示一个有两种情况的判别联合:Success 和 Failure。 Success 情况包含一个 PdfDocument,而 Failure 情况持有错误消息。 GeneratePdf 方法接收一个 HTML 字符串,尝试使用 IronPDF 生成一个 PDF,并将结果作为 PdfResult 返回。 如果 PDF 生成成功,它将返回 Success 情况,以及生成的 PDF。 如果发生异常,它会返回带有错误消息的 Failure 情况。
结论
C#中的可辨识联合提供了一种强大且灵活的方法来建模具有多种可能情况的数据。 虽然C#不支持可辨识联合,但您可以使用类层次结构、模式匹配和其他技术对其进行模拟。 生成的代码更加类型安全,更不容易出错,并且更易于维护。
IronPDF提供免费试用,帮助您免费体验软件。 您可以探索所有功能,并查看它们如何符合您的需求。 试用结束后,许可证起价为 $999。
Jacob Mellor 是 Iron Software 的首席技术官,也是一位开创 C# PDF 技术的有远见的工程师。作为 Iron Software 核心代码库的原始开发者,他从公司成立之初就开始塑造公司的产品架构,与首席执行官 Cameron Rimington 一起将公司转变为一家拥有 50 多名员工的公司,为 NASA、特斯拉和全球政府机构提供服务。