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


113

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

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

リストのIEnumerableを公開すると、不変になります。
クラスを不変にするために従う必要のある基本的なルールは何ですか?


2
不変性に関するEric Lippertのブログシリーズ、特に「不変性の種類」のエントリを読むことを強くお勧めします。「リストのIEnumerableを公開すると不変になる」というコメントは、私には少し奇妙に思えます。それはどういう意味ですか?
Jon Skeet

私が意味したのは、リストへのアクセスを許可するのではなく(オブジェクトに他のオブジェクトのリストがある場合)、ユーザーがIEnumerableでメンバーにアクセスできるようにすることでした。ここでは特定の例のようにトーキングリストを使用していますが、どのようなデータ構造でもかまいません。
Biswanath

1
Jon SkeetのEric Lippertのブログシリーズへのリンクは、MSDNがこれらのブログをアーカイブしたときに壊れています。「不変性の種類」をご覧ください。シリーズの残りの部分は、左側のパネルで2007年12月+ 2008年1月の下にあるようです。
John Doe

人々は一般的に用語を混同する方法にエリックリペットのブログシリーズを参照してくださいatomicityvolatilityimmutability第一部第二部、および第三部。これらは彼の個人的なブログからのものであり、彼のMSDNの投稿よりも初心者にやさしいと思います。
John Doe

回答:


121

私はあなたが正しい軌道に乗っていると思います-

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

ラッパールックアッププロパティを記述すれば、ルックアップのみを実行できますか?そして答えてくれてありがとう。
ビスワナート

5
コンストラクタに引数として渡された変更可能な参照型はすべてコピーする必要があります。それ以外の場合、呼び出し元は引き続き状態への参照を保持します。
Brian Rasmussen、

@Brian Rasmussenのとおりですが、コピーされた場合でも、提供されているacces.sorsによっては、呼び出し元が可変オブジェクトにアクセスできる可能性があります。可変オブジェクトが渡された場合、クラスは常にオブジェクトの別のコピーまたは不変セクションを返すのが最善です
Blair Conrad

@Blair-ルックアップに使用したいクラスに辞書がある場合。ルックアップを行う読み取り専用プロパティは問題ないでしょうか?
ビスワナート

@Biswanath-はい、ディクショナリの値が変更可能な場合、変更したくない場合は、返す前にそれらを保護する必要があるという警告があります
Blair Conrad

18

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

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

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

}

1
ImmutableListのほうがいいと思います。
Kevin Wong

ImmutableListを使用し、クラスのシールも検討してください
kofifus

ImmutableListがこの使用例に適しているとは思いませんbasic rules to make a class (that contains a list) immutable。ImmutableListセマンティクスを使用すると、リストのコピーに項目を追加するときにメモリをより効率的に使用できますが、これはより具体的な使用例のようです。
Joe

11

また、次の点にも注意してください。

public readonly object[] MyObjects;

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


5

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; }
}

1

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


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.