不変になる前は、多くの API でIEnumerableが頼りになるインターフェイスでした。これには、API が渡されたオブジェクトの実際の型に影響されないという利点があったためです。
public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings)
{
foreach (var thing in collectionOfThings) doSomethingWith(thing);
foreach (var thing in collectionOfThings) doSomethingElseWith(thing);
}
もちろん、これには少なくとも 2 つの欠点があります。
API の背後にあるコードは、 collectionOfThingsの不変性に依存できず、「コレクションが変更されました」例外が発生したり、その他の微妙な問題が発生したりする可能性があります。
collectionOfThingsが実際のコレクションなのか、単に遅延クエリなのかはわかりません。それが実際のコレクションであると想定し、そうでない場合、複数の列挙を実行することでパフォーマンスが低下するリスクがあります。それが遅延クエリであり、実際には実際のコレクションであると仮定した場合、それをローカル リストまたは他の凍結されたコレクションに変換すると、最初の問題から保護するのに役立ちますが、不必要なコストが発生します (「ToList」操作の実行中に競合状態が依然として発生します)。 )。明らかに、これをチェックして「正しいこと」を実行するために少量のコードを作成することはできますが、これは面倒な余分な混乱です。
命名規則を使用する以外に、これに対処するための満足のいくパターンを見つけたことがないことを認めなければなりません。実用的なアプローチは、 IEnumerableがコレクションを渡すための最も摩擦の少ないアプローチであると思われましたが、欠点はありました。
現在、不変のコレクションにより、状況は大幅に改善されています...
public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings)
{
コレクションが変更されるリスクがなくなり、複数の列挙によるパフォーマンスへの影響についてあいまいさがなくなりました。
ただし、ImmutableListを渡す必要があるため、明らかに API の柔軟性が失われています。私たちのクライアントが他の種類の列挙可能な不変コレクションを持っている場合、それを列挙することだけが目的であっても、それを消費するにはImmutableListにコピーする必要があります。
理想的には、次のようなインターフェースを使用できます
public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings)
しかしもちろん、インターフェイスは、慣習による場合を除いて、不変性などのセマンティクスを強制することはできません。
基本クラスを使用するとうまくいくかもしれません
public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings)
ただし、サブクラスが可変性を導入しないように、封印されていない不変クラスを作成することは悪い形式と見なされます。いずれにせよ、これは BCL では行われていません。
または、API で IEnumerable を使用し続け、命名規則を使用して、コードが渡される不変のコレクションに依存していることを明確にすることもできます。
だから...私の質問は、不変のコレクションを渡すときにどのパターンが最適と考えられるかということです? ImmutableListは、不変性を使い始めたら「新しいIEnumerable 」ですか、それとももっと良い方法がありますか?
アップデート
IReadOnlyCollection (以下の Yuval Itzchakov によって提案されています) はIEnumerableよりも明らかに改善されていますが、コレクション内の制御されていない変更からコンシューマーを完全に保護することはできません。Roslyn コードベースが (主にImmutableArrayを介して) 不変性を多用し、これらを他のメソッドに渡すときに明示的な型指定を使用しているように見えることは注目に値しますが、 ImmutableListがIEnumerableを受け入れるメソッドに渡される場所がいくつかあります。