使いこなせていなかった`System.Collections.Immutable`

## はじめに

Collections系の記事を書こうとしていたら、`System.Collections.Immutable`名前空間の型をまったく使えていなかったことに気づき、反省も兼ねて記事にしてみました。

仕様は調べて書いているつもりですが、 使いこなせていない人(=私)の記事ですので、実戦で使いこなしている方からの温かいツッコミを期待しています！

## Immutable(イミュータブル／変化しない)

まずは_Immutable_なデータ型の代表的な特徴は以下の通り。

- 内容に変更がない場合の複製のコストが低い
- スレッドセーフ

これを意識したクラスを自分で作ったことがあるかどうかはさておき、**使ったことがない人はいない**と思います。
なぜならば、C#では **`string`型はImmutable** だからです。
「Immutableってなんだったっけ？」と思ったら、`string`型を思い出すと良いでしょう。

## クラスライブラリの中身

`System.Collections.Immutable`名前空間には、一通りのコレクション系クラスが揃っています。

|型|種別|整列|
|:--|:--|:--|
|`ImmutableArray<T>`|配列|〇|
|`ImmutableList<T>`|List|〇|
|`ImmutableHashSet<T>`|Set|×|
|`ImmutableSortedSet<T>`|Set|〇|
|`ImmutableQueue<T>`|Queue|〇|
|`ImmutableStack<T>`|Stack|〇|
|`ImmutableDictionary<TKey,TValue>`|Dictionary|×|
|`ImmutableSortedDictionary<TKey,TValue>`|Dictionary|〇|

### インスタンスの生成

これらのクラスはコンストラクタ持ちませんが、C#12 以降では、コレクション式で生成できます。
C#11 以前の環境では、対応する静的クラスの**ファクトリメソッド`Create<T>()`からインスタンス生成**をしましょう。

```csharp:Immutableなリストの生成 (C# 11 以前)
var list = ImmutableList.Create<int>(2, 3, 5, 7);
```

```csharp:《参考》MutableなListの生成 (C# 11 以前)
var list = new List<int>() { 2, 3, 5, 7 };
```

```csharp:各リストの生成 (C# 12 以降)
// 「コレクション式」構文により、
// 統一感のある書き方ができるようになった。
ImmutableList<int> ilist = [2, 3, 5, 7];
List<int> list = [2, 3, 5, 7];
int[] array = [2, 3, 5, 7];
ReadOnlySpan<int> span = [2, 3, 5, 7];
```

### コレクションの操作

同じようなメソッド名のコレクション操作を持ちますが、操作後は**別のインスタンスが生成される**ことに注意します。

```csharp:ImmutableなListに項目追加
// ImmutableなListは、項目が追加された別のインスタンスを生成する
var addedList = list.Add(11);
```

```csharp:ImmutableなListに項目追加 (C# 12 以降)
// 以下の書き方でも末尾に項目追加可能。
// パフォーマンスは不明
list = [.. list, 11];
```

```csharp:《参考》MutableなListに項目追加
// MutableなListは、自身の中身を書き換える
list.Add(11);
```

_いざ使うときに左辺を書き忘れそうで怖い……_

### Builder

不変なデータ型を扱うときに欠かせない、**Builder**がそれぞれのImmutableコレクション型に存在しており、`ToBuilder()`メソッドでBuilderに、`ToImmutable()`メソッドで元のImmutableコレクションに、それぞれ移行できます。
ちょうど、`String`型に対する **`StringBuilder`型のようなもの**だと思えば、しっくりくると思います。

```csharp:Builderの使い方
// var originalList = ImmutableList.Create<int>(2, 3, 5, 7); (C# 11 以前)
ImmutableList<int> originalList = [2, 3, 5, 7];
// 大量の変更が必要な場合、Builderに。
var builder = originalList.ToBuilder();
builder.Add(11);
builder.Add(13);
builder.Remove(2);
// 変更後、ToImmutable()で、Immutableに。
var newList = builder.ToImmutable();
```

```csharp:《参考》StringBuilderに似ている
var originalString = "ABCDEFG";
// 大量の変更が必要な場合、Builderに。
var builder = new StringBuilder(originalString);
builder.Append("H");
builder.Append("I");
builder.Remove(0, 1);
// Immutableに。
var newStr = builder.ToString();
```

初めからBuilderを生成するためのファクトリメソッドもあります。

```csharp:初めからBuilderを生成する
var builder = ImmutableList.CreateBuilder<int>();
```

## どんな時に使う？

"マルチスレッドな操作で" "コレクションそのものを扱うとき" **Immutableなコレクション**が生きてくると思います。同様にマルチスレッド操作で登場する**Concurrentなコレクション**とは役割が違うので、それぞれを適した場面で使うことになります。
また、**ReadOnlyなインタフェース**とも似ているようですが、出番が全く違います。

|型|性質|
|:--|:--|
|`System.Collections.Immutable.`<br>`ImmutableHogehoge`|不変だが、コレクション操作は行う<br>コレクション全体をスレッドセーフに扱いたい|
|`System.Collections.Concurrent.`<br>`ConcurrentHogehoge`|可変<br>コレクション操作をスレッドセーフに扱いたい|
|`System.Collections.Generic.`<br>`IReadOnlyHogehoge`|コレクション操作を行わない<br>スレッドセーフかは実装依存(スレッドセーフとはいえない)|

## まとめ

`System.Collections.Immutable`名前空間がマルチスレッドの全てを解決するわけではないですが、この存在を認識していれば **必ず活躍する場がある** と思います。実際に使用する際には`ImmutableInterlocked`クラスなども合わせて、うまく組み立てる必要がありそうですので、試行錯誤しつつ使っていきたいと思います。