.NETの開発でよく誤解されるのは、IEnumerable型やReadOnlyCollection型の変数がスレッドセーフだという考え方だ。MicrosoftのAndrew Arnott氏は次のように説明している。
ReadOnlyCollection<T>やIReadOnlyList<T>、IEnumerable<T>を渡された時、唯一保証されるのは自分でデータを変更できないことです。コレクションを渡した人が、データを変更しないことを保証するものではありません。それでも、データが変わらないという確信を持たなければならない時もあるでしょう。これらの型は、内容の変更を通知するイベントを提供していませんし、変更するとしても、ひょっとしたらその内容を列挙している間に別のスレッドで実行されるかもしれません。このようなふるまいは、アプリケーション内でデータの破損やランダムな例外につながる可能性があります。
IEnumerableやReadOnlyCollectionを使いたくなるようなシナリオで、真にスレッドセーフなコレクションを提供するために、MicrosoftのBase Class Library (BCL) チームは、新しい不変コレクションのプレビューを提供している。関数型プログラミング言語で見つけられた技術に基づいて、普通はコレクションを変化させるメソッドが、その代わりに新しいコレクションを作り出す。データ共有は、古いコレクションと新しいコレクションの間で可能になり、効率的になる。
不変コレクションの興味深い特徴は、パブリックコンストラクタを持たないことだ。代わりに、作業は、常にImmutableXxx<T>.Emptyで始まる。Andrew氏は次のように述べている。
スタティックなEmptyプロパティを使うことは、よく確立したパターンを抜け出すことであり、それには理由があります。コンストラクタは、オブジェクトを割り当てなければなりません。これらのコレクションを不変にするには、新しいオブジェクトを空のリストにしなければなりません。コレクションを変更すると新しいコレクションができ、コンストラクタを使うと空のリストを表す複数のオブジェクトを作り出すことになります。これは無駄なことです。スタティックなプロパティを使えば、アプリ内のすべてのコードで共有できる空のリストであるシングルトンを返せるようになります。
ビルダとコレクション
不変コレクションの構築は、メモリの割り当てのために非常にコストがかかる。同様のことは、charの不変コレクションであるstringでも起こっている。これを解決するために、不変コレクションはToBuilderメソッドを公開するだろう。これにより、コストをかけずに修正できるビルダオブジェクトを返す。その後は、不変コレクションを再取得するToImmutable を使えばよい。
パフォーマンス
不変コレクションのパフォーマンスは、少々扱いにくい。以下のチャートから分かるように、不変コレクションの順番は、実際にはかなりよい。そして、首尾一貫している。コレクションの内部配列の最大サイズを超えるとコレクションを完全にコピーしなければならなくなるが、そのことは心配しなくてよい。普通のコレクションとは異なり、不変コレクションはアイテムが削除されると、未使用スペースを実際には開放するだろう。
しかし、そこにはコストが発生する。操作毎に、メモリ内に他のオブジェクトを割り当てなければならず、そのためにガベージコレクタを精一杯使うことになる。最大の勝利は、コレクションのスナップショットのコピーを作るときだ。しかし、最後にお勧めするのは、「役割を果たす簡単なコードを使い、必要があれば、パフォーマンスチューニングをする」ことだ。
このプレビューは、Microsoft.Bcl.Immutable NuGet packageで参照できる。