C#で不変性を使用する方法

不変性は関数型プログラミング言語の機能であり、プログラムの作成、テスト、および保守を容易にします。ただし、不変性は多くの命令型プログラミング言語ではサポートされていません。最近まで、C#はすぐに使用できる不変性をサポートしていませんでした。 

これは、.NET 5でプレビューできるC#9でのレコードの導入によって変わります。ただし、NuGetパッケージとして利用できるSystem.Collections.Immutable名前空間を使用することで、以前のバージョンのC#で不変性を実装できます。 

不変オブジェクトは、作成後に変更できないオブジェクトとして定義されます。データ転送オブジェクトなどの多くのユースケースでは、不変性が望ましい機能です。この記事では、不変性を利用する理由と、C#で不変性を実装する方法について説明します。

この記事で提供されているコード例を使用するには、システムにVisual Studio2019がインストールされている必要があります。まだコピーをお持ちでない場合は、こちらからVisual Studio2019をダウンロードできます。 

VisualStudioで.NETCoreコンソールアプリケーションプロジェクトを作成します

まず、VisualStudioで.NETCoreコンソールアプリケーションプロジェクトを作成しましょう。Visual Studio 2019がシステムにインストールされていると仮定して、以下に概説する手順に従って、VisualStudioで新しい.NETCoreコンソールアプリケーションプロジェクトを作成します。

  1. Visual StudioIDEを起動します。
  2. 「新しいプロジェクトの作成」をクリックします。
  3. 「新規プロジェクトの作成」ウィンドウで、表示されたテンプレートのリストから「コンソールアプリ(.NETCore)」を選択します。
  4. [次へ]をクリックします。 
  5. 次に表示される[新しいプロジェクトの構成]ウィンドウで、新しいプロジェクトの名前と場所を指定します。
  6. [作成]をクリックします。 

これにより、Visual Studio2019で新しい.NETCoreコンソールアプリケーションプロジェクトが作成されます。このプロジェクトを使用して、この記事の後続のセクションで不変性を説明します。

System.Collection.ImmutableNuGetパッケージをインストールします

不変の型を操作するには、NuGetからSystem.Collections.Immutableパッケージをインストールする必要があります。これは、Visual Studio 2019 IDE内のNuGetパッケージマネージャーを使用するか、NuGetパッケージマネージャーコンソールで次のコマンドを実行することによって実行できます。

インストールパッケージSystem.Collections.Immutable

このパッケージは、不変コレクションとも呼ばれるスレッドセーフクラスのコレクションで構成されています。

C#9の不変性と記録を理解する

データ転送オブジェクトは、不変性が必要な場合の典型的な例です。DTOのインスタンスは、コンシューマー側で使用されるテクノロジーから独立できるようにシリアル化されることがよくあります。当然、データベースとクライアント間でデータオブジェクトを転送するときは、オブジェクトを変更できないようにする必要があります。これがまさにDTOの目的です。C#でのデータ転送オブジェクトの使用について詳しくは、以前の記事をご覧ください。 

不変のDTOを作成するには、ReadOnlyCollectionまたはSystem.Collections.Immutable名前空間のスレッドセーフな不変のコレクションタイプを利用できます。または、C#9のレコードタイプを利用して、不変のDTOを実装することもできます。

C#9のレコード型は、読み取り専用プロパティのみを持つ軽量で不変のデータ型(または軽量クラス)です。レコードタイプは不変であるため、スレッドセーフであり、作成後に変更または変更することはできません。

レコードタイプは、コンストラクター内でのみ初期化できます。クラス(この例では作成者)のレコード型の作成は、次のコードスニペットと同じくらい簡単です。

クラスデータAuthor(int Id、string firstName、string lastName、string address);

以下に示すコードスニペットに示すように、作成者レコードタイプを記述することもできます。

パブリックデータクラス作成者{

    public int Id {get; 初期化; }

    public string firstName {get; 初期化; }

    public string lastName {get; 初期化; }

    パブリック文字列アドレス{取得; 初期化; }

}

レコードタイプを宣言するときは、dataキーワードの使用法に注意してください。クラスの宣言で使用されるdataキーワードは、タイプをレコードとしてマークします。レコードタイプのインスタンスを利用して、レイヤー間でデータを渡すと同時に、DTOの不変性を確保できます。

System.Collections.Immutable名前空間

不変のコレクションとは、一度作成されたメンバーを変更できないコレクションです。System.Collections.Immutable名前空間は、いくつかの不変のコレクションで構成されています。この名前空間には、不変バージョンのリスト、辞書、配列、ハッシュ、スタック、およびキューが含まれています。

ImmutableStackは、可変スタックの場合とほぼ同じ方法で要素をプッシュおよびポップするために使用できます。ただし、ImmutableStackは不変のコレクションであるため、その要素を変更することはできません。したがって、popメソッドを呼び出してスタックから要素をポップすると、新しいスタックが作成され、元のスタックは変更されません。

これを例で説明しましょう。次のコードスニペットは、要素を不変のスタックにプッシュする方法を示しています。

var stack = ImmutableStack.Empty;

for(int i = 0; i <10; i ++)

{{

    stack = stack.Push(i);

}

次のプログラムは、不変スタックの要素を変更できないことを示しています。

クラスプログラム

    {{      

        static void Main(string [] args)

        {{

            var stack = ImmutableStack.Empty;

            for(int i = 0; i <10; i ++)

            {{

                stack = stack.Push(i);

            }

            Console.WriteLine( "元のスタックの要素数:

             "+ stack.Count());

            var newStack = stack.Pop();

            Console.WriteLine( "新しいスタックの要素数:" +

            newStack.Count());

            Console.ReadKey();

        }

    }

上記のプログラムを実行すると、コンソールウィンドウに出力がどのように表示されるかを次に示します。

図1に示すように、元の不変スタック(10個の要素を含む)は、Pop()メソッドを呼び出した後も変更されていません。むしろ、9つの要素で新しい不変のスタックが作成されます。

不変のコレクションはコンストラクターを提供しませんが、以下に示すコードスニペットに示すように、Createと呼ばれる静的ファクトリメソッドを利用できます。

var list = ImmutableList.Create(1、2、3、4、5);

このコレクションに要素を追加または削除する場合は、新しい不変リストが作成され、元の不変リストは変更されません。

不変性は設計上の選択です。これは、タイプのインスタンスが作成された後は変更できないことを意味します。不変のスタックと不変のキューを除いて、すべての不変のコレクションはAVLツリーに基づいています。したがって、ツリー全体をコピーしなくても、コレクションの任意の位置(開始、中間、または終了)に要素を挿入できます。

C#でさらに多くのことを行う方法:

  • C#でデータ注釈を使用する方法
  • C#8でGUIDを操作する方法
  • C#で抽象クラスとインターフェイスを使用する場合
  • C#でAutoMapperを操作する方法
  • C#でラムダ式を使用する方法
  • C#でAction、Func、およびPredicateデリゲートを操作する方法
  • C#でデリゲートを操作する方法
  • C#で簡単なロガーを実装する方法
  • C#で属性を操作する方法
  • C#でlog4netを操作する方法
  • C#でリポジトリデザインパターンを実装する方法
  • C#でリフレクションを操作する方法
  • C#でfilesystemwatcherを操作する方法
  • C#でレイジー初期化を実行する方法
  • C#でMSMQを操作する方法
  • C#で拡張メソッドを操作する方法
  • C#でラムダ式を使用する方法
  • C#でvolatileキーワードを使用する場合
  • C#でyieldキーワードを使用する方法
  • C#でポリモーフィズムを実装する方法
  • C#で独自のタスクスケジューラを構築する方法
  • C#でRabbitMQを操作する方法
  • C#でタプルを操作する方法
  • C#での仮想メソッドと抽象メソッドの調査
  • C#でDapperORMを使用する方法
  • C#でフライウェイトデザインパターンを使用する方法