不変性は、C# が成熟しつつある領域です。これまでのところ、C# はconst
C++ のセマンティクスを採用していません。これは実際には良いことだと思います。C++での の動作によりconst
、希望どおりに機能するクラス階層を設計することが困難になることがよくありました。const_cast<>
望ましくない constness をバイパスするためにコードが散りばめられているのを見るのは珍しいことではありませんでした。願わくば、C# の設計者が、より単純でありながら表現力のある代替手段を考案してくれることを願っています。
現在、メソッドに渡されるパラメーターまたはオブジェクトを不変としてマークする言語機能はありません。できる最善の方法は、読み取り操作のみを許可するインターフェイス (またはラッパー) を使用してオブジェクトを渡すことです。
.NET の標準コレクションの一部では、ラッパーを使用できます。このReadOnlyCollection
ラッパーは、可変ICollection
型を読み取り専用コンテナー内にカプセル化します。
不変型を作成するには、言語機能の計画と認識が必要です。たとえば、readonly
キーワードはあなたの友達です。クラスまたは構造体のメンバーを不変として宣言できます。残念ながら、この不変性は参照にのみ適用され、参照されるオブジェクトのメンバーには適用されません。これが意味することは、次のように宣言できるということです。
private readonly int[] m_Values = new int[100];
public void SomeMethod()
{
m_Values = new int[50]; // illegal, won't compile!
m_Values[10] = 42; // perfectly legal, yet undesirable
}
上記の例では、配列への参照は不変ですが、配列の個々の要素はそうではありません。もちろん、この動作は配列を超えて拡張されます。
不変の型を設計するときに役立つと私が思った方法は、不変の動作を独自のインターフェイスに分離し、それをデータを管理するクラスによって実装することです。インターフェイスは、オブジェクトの状態を変更しないことが保証されている get プロパティとメソッドのみを公開します。その後、タイプのインスタンスをそのインターフェイス タイプのパラメータとしてメソッドに渡すことができます。これは、不変性サポートの弱い形式です。呼び出されたメソッドは、多くの場合、可変型への参照をキャストできるためです。より良いが、より面倒な代替手段は、同じインターフェースを実装し、実際のインスタンスへの参照を維持するラッパー実装を作成することです (ほとんどの場合ReadOnlyCollection
)。これは手間がかかりますが、不変性をより強力に保証します。
使用するアプローチは、不変性の保証がどれほど重要であるか、およびそこに到達するためにどれだけの時間と労力を費やしても構わないと思っているかによって異なります。
このトピックの詳細に興味がある場合は、Eric Lippert がC# の不変性に関する優れた一連の記事を公開しています。