.NET ヘルプ

C# Discriminated Union (開発者のための仕組み)

判別共用体は、タグ付き共用体や和型とも呼ばれ、異なる形を取ることができるデータをモデル化するための強力なツールであり、明確に定義され、限られた可能なケースが存在します。 C#には、他の言語(例えばF#やRust)のようなネイティブの識別共用体はありませんが、言語内でいくつかのテクニックを使って識別共用体をシミュレートすることができます。 このチュートリアルでは、識別された統合に深く入り込み、それをC#で実装する方法と<IronPDFライブラリを使った実用的な使い方について説明します。

差別連合とは何ですか?

簡単に言うと、識別付き結合は、いくつかの定義済みの形式または値のいずれかを保持できる型です。 これは、コンパイル時に有効なケースのみが処理されることを保証しながら、異なる型や値をカプセル化するタイプセーフ構造を作成する方法を提供します。

ある操作の結果を表すシナリオを想像してください。 操作は成功し、何らかのデータを返すか、失敗し、エラーメッセージを返します。 識別付き結合を使用すると、これらの2つの可能な結果を1つの型で表すことができます。

例C#と#numで判別ユニオンをシミュレートする;

以下は、クラス構造を使用して、C#で差別結合をシミュレートする方法の例です:

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
$vbLabelText   $csharpLabel

この例では、OperationResult は、識別共用体型を表す抽象クラスです。 T型の値を持つSuccessか、エラーメッセージを持つFailureのいずれかになります。 privateコンストラクタは、このようなクラスのインスタンスが定義済みのケースを通してのみ作成できることを保証します。

識別されたユニオンを使用したパターンマッチング

C#は強力なパターンマッチング機能を備えており、差別化された組合わせでうまく機能します。 OperationResultの例を拡張し、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")
'	};
$vbLabelText   $csharpLabel

ここでのスイッチ式は、OperationResult の成功ケースと失敗ケースの両方を処理します。 これにより、コンパイル時に考えられるすべてのケースがカバーされ、型安全性が提供され、実行時エラーのリスクが低減されます。

差別化ユニオンの拡張メソッド

あなたは、拡張メソッドを使用して、差別化ユニオンの機能を拡張することができます。 たとえば、結果が成功であるかを判断するために、OperationResult の拡張メソッドを作成しましょう。

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
$vbLabelText   $csharpLabel

この public static bool メソッドは、結果が Success ケースのインスタンスであるかどうかをチェックします。

C&numの差別化ユニオンのネイティブサポート;

C#は、他の言語のような差別的結合をネイティブでサポートしていませんが、そのような機能を追加することについてコミュニティで継続的に議論されています。 ネイティブの差別化ユニオンは、クラス階層に依存することなく、ユニオン型の定義と作業を容易にします。

コンパイラのエラーと型安全性

識別組合の主な利点の1つは、安全性のタイプです。コンパイル時にすべての可能なケースを知ることができるので、コンパイラーはすべてのケースを処理するように強制することができます。 これにより、実行時のエラーが減り、エラーが発生しにくいコードになります。

例えば、switch文の中で特定のcaseを処理し忘れた場合、コンパイラはエラーを出し、足りないcaseに対処するよう促します。 これは、複数のケースが考えられる複雑なデータ構造を扱う場合に特に役立ちます。

IronPdfとC#の識別されたユニオンの使用;

C# 判別共用体(開発者のための動作方法):図 1 - IronPDF

IronPDFは、開発者がHTMLからPDFファイルを作成し、手間をかけずにPDFファイルを修正できるC#のPDFライブラリです。 C#でPDFを扱う場合、IronPDFを差別化ユニオンと統合することで、PDFファイルを生成または処理する際に異なるシナリオを扱うことができます。 例えば、PDFの生成に成功したり、エラーが発生したりするプロセスがあるとします。 差別化ユニオンを使用すると、このプロセスを明確にモデル化できます。 IronPDFを使ってPDFを生成し、その結果を判別された結合として返す簡単な例を作ってみましょう。

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
$vbLabelText   $csharpLabel

PdfResultクラスは、2つのケースを持つ差別結合を表します:成功と失敗です。 成功ケースには PdfDocument が含まれ、失敗ケースにはエラーメッセージが含まれます。 GeneratePdfメソッドはHTML文字列を受け取り、IronPDFを使ってPDFを生成し、その結果をPdfResultとして返します。 PDF生成に成功した場合、生成されたPDFとともにSuccessケースを返します。 例外が発生した場合は、エラーメッセージとともにFailure caseを返します。

結論

C# 判別共用体 (開発者向けの仕組み): 図 2 - ライセンス

C#の弁別結合は、複数の可能なケースを持つデータをモデル化するための強力で柔軟な方法を提供します。 C#は識別付き結合をサポートしていませんが、クラス階層やパターンマッチング、その他のテクニックを使ってシミュレートすることができます。 その結果、より型安全で、エラーが少なく、保守しやすいコードになります。

IronPDFは、事前の費用をかけずにソフトウェアを体験するための無料トライアルを提供しています。 すべての機能を調べ、あなたのニーズとどのように一致するかを確認することができます。 トライアル終了後、ライセンスは$749から利用できます。

チペゴ
ソフトウェアエンジニア
チペゴは優れた傾聴能力を持ち、それが顧客の問題を理解し、賢明な解決策を提供する助けとなっています。彼は情報技術の学士号を取得後、2023年にIron Softwareチームに加わりました。現在、彼はIronPDFとIronOCRの2つの製品に注力していますが、顧客をサポートする新しい方法を見つけるにつれて、他の製品に関する知識も日々成長しています。Iron Softwareでの協力的な生活を楽しんでおり、さまざまな経験を持つチームメンバーが集まり、効果的で革新的な解決策を提供することに貢献しています。チペゴがデスクを離れているときは、良い本を楽しんだり、サッカーをしていることが多いです。
< 以前
C# HttpClient (開発者のための仕組み)
次へ >
C#新しいGUID(開発者のための仕組み)