web-dev-qa-db-ja.com

不変のクラスを作成するにはどうすればよいですか?

不変クラスの作成に取り組んでいます。
すべてのプロパティを読み取り専用としてマークしました。

クラス内のアイテムのリストがあります。
プロパティが読み取り専用の場合、リストは変更できます。

リストのIEnumerableを公開すると、不変になります。
クラスを不変にするために従わなければならない基本的なルールは何か知りたいですか?

106
Biswanath

あなたは正しい軌道に乗っていると思う-

  • クラスに注入されるすべての情報は、コンストラクターで提供される必要があります
  • すべてのプロパティはゲッターのみである必要があります
  • コレクション(または配列)がコンストラクターに渡された場合、それをコピーして、呼び出し元が後で修正しないようにする必要があります。
  • コレクションを返す場合は、コピーまたは読み取り専用バージョンを返します(たとえば、 ArrayList.ReadOnly などを使用します。これを前のポイントとstore呼び出し元がアクセスしたときに返される読み取り専用コピー)、列挙子を返す、またはコレクションへの読み取り専用アクセスを許可する他のメソッド/プロパティを使用する
  • メンバーのいずれかが可変である場合、可変クラスの外観が引き続き存在する可能性があることに注意してください-この場合、保持する状態をすべてコピーして、可変オブジェクト全体を返さないようにする必要があります。呼び出し元に返す前に-別のオプションは、変更可能なオブジェクトの不変の「セクション」のみを返すことです-この点を拡張するよう促してくれた@Brian Rasmussenに感謝します
116
Blair Conrad

不変にするには、すべてのプロパティとフィールドを読み取り専用にする必要があります。そして、リスト内のアイテム自体は不変でなければなりません。

次のように、読み取り専用リストプロパティを作成できます。

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}
17
Joe

また、次のことに注意してください。

public readonly object[] MyObjects;

readonlyキーワードでマークされていても不変ではありません。インデックスアクセサーによって個々の配列の参照/値を変更することもできます。

11
lubos hasko

ReadOnlyCollectionクラスを使用します。 System.Collections.ObjectModel名前空間にあります。

リスト(またはコンストラクター内)を返すものについては、リストを読み取り専用コレクションとして設定します。

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}
4
mbillard

別のオプションは、内部コレクションをまったく公開するのではなく、訪問者パターンを使用することです。

1
Andrew