不変フィールドのセッターを処理するにはどうすればよいですか?


25

2つのreadonly intフィールドを持つクラスがあります。プロパティとして公開されます:

public class Thing
{
    private readonly int _foo, _bar;

    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public int Foo { get { return _foo; } set { } }

    public int Bar { get { return _bar; } set { } }
}

ただし、次のコードは完全に合法なコードです。

Thing iThoughtThisWasMutable = new Thing(1, 42);

iThoughtThisWasMutable.Foo = 99;  // <-- Poor, mistaken developer.
                                  //     He surely has bugs now. :-(

それが機能すると仮定することから生じるバグは、sidなものであるに違いありません。確かに、間違った開発者はドキュメントを読んでいたはずです。しかし、それは問題をコンパイルまたは実行時エラーで警告しなかったという事実を変えるものではありません。

Thing開発者が上記の間違いを犯さないように、クラスをどのように変更する必要がありますか?

例外を投げますか?プロパティの代わりにゲッターメソッドを使用しますか?



1
ありがとう、@ gnat。その投稿(質問と回答の両方)は、Capital Iのように、インターフェイスについて話しているようInterfacesです。私はそれが私がやっていることだかわからない。
kdbanman

6
@gnat、それは恐ろしいアドバイスです。インターフェイスは、テストが容易な、公的に不変のVO / DTOを提供する優れた方法を提供します。
デビッドアルノ

4
@gnat、私はそうしましたが、OPはインターフェイスが不変性を破壊すると考えているように見えるので、質問は意味がありません。これはナンセンスです。
デビッドアルノ

1
@gnat、その質問はDTOのインターフェースの宣言に関するものでした。上位の回答は、インターフェイスは有害ではないが、おそらく不要であるというものでした。
ポールドレイパー

回答:


107

なぜそのコードを合法にするのですか?何もしない場合は
取り出しset { }ます。
これは、読み取り専用のパブリックプロパティを定義する方法です。

public int Foo { get { return _foo; } }

3
何らかの理由でそれが合法であるとは思わなかったが、完全に機能するようだ! パーフェクト、ありがとう。
kdbanman

1
@kdbanmanおそらく、ある時点でIDEが実行しているのを見たことと、おそらくはオートコンプリートが関係しています。
パンツァークライシス

2
@kdbanman; (C#5まで)違法なのはpublic int Foo { get; }(ゲッターのみの自動プロパティ)public int Foo { get; private set; }です。
ローランラリッツァ

@LaurentLARIZZA、あなたは私の記憶をジョギングしました。それがまさに私の混乱の原因です。
kdbanman

45

C#5以前では、ゲッターを介して公開される不変フィールドの2つのオプションに直面していました。

  1. 読み取り専用のバッキング変数を作成し、手動ゲッターを介してそれを返します。このオプションは安全です(readonly不変性を破壊するために明示的に削除する必要があります。多くの定型コードを作成しました)。
  2. プライベートセッターで自動プロパティを使用します。これにより、より単純なコードが作成されますが、不変性を誤って破りやすくなります。

ただし、C#6(昨日リリースされたVS2015で利用可能)では、読み取り専用の自動プロパティという両方の長所を利用できるようになりました。これにより、OPのクラスを次のように簡素化できます。

public class Thing
{
    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; }
    public int Bar { get; }
}

Foo = fooそしてBar = barラインはなく(より簡単なコードを達成する)明示的に定義されるように有するよりも、暗示される(ロバストな読み取り専用の要件を達成する)コンストラクタとバッキングフィールドでのみ有効です。


2
そして、主要なコンストラクターは、コンストラクターの構文オーバーヘッドを取り除くことにより、これをさらに簡素化できます。
セバスチャンレッド


1
@DanLyonsくそー、コードベース全体でこれを使用することを楽しみにしていました。
セバスチャンレッド

12

セッターを取り除くことができます。彼らは何もせず、ユーザーを混乱させ、バグを引き起こします。ただし、代わりにそれらをプライベートにして、バッキング変数を取り除き、コードを単純化することができます。

public class Thing
{
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; private set; }

    public int Bar { get; private set; }
}

1
" public int Foo { get; private set; }"そのクラスはそれ自体を変更できるため、もはや不変ではありません。でもありがとう!
kdbanman

4
記述されているクラスは不変であり、それ自体は変更されません。
デビッドアルノ

1
私の実際のクラスは、私が与えたMWEよりも少し複雑です。私は本当の不変性の後、スレッドの安全性とクラス内部の保証を完備しています。
kdbanman

4
@kdbanman、あなたのポイントは?クラスが構築中にプライベートセッターのみに書き込む場合、「実際の不変性」が実装されています。readonlyキーワードはちょうどポカヨケで、それは将来的に不変性を破るクラスに対してガードすなわち、ただし、不変性を実現する必要はありません。
デビッドアルノ

3
興味深い用語、私はそれをグーグルで調べなければなりませんでした-ポカヨケは「間違い防止」を意味する日本語の用語です。あなたが正しい!それはまさに私がやっていることです。
kdbanman

6

2つのソリューション。

シンプル:

不変の読み取り専用オブジェクトについて、Davidが指摘したセッターを含めないでください。

代わりに:

セッターが新しい不変オブジェクトを返すことを許可します。前者と比較して冗長ですが、初期化された各オブジェクトの状態を経時的に提供します。この設計は、すべての命令型OOP言語に及ぶ、スレッドの安全性と不変性のための非常に便利なツールです。

擬似コード

public class Thing
{

{readonly vars}

    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

パブリックスタティックファクトリ

public class Thing
{

{readonly vars}

    private Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public static with(int foo, int bar) {
        return new Thing(foo, bar)
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

2
setXYZはファクトリメソッドの恐ろしい名前です。withXYZなどを検討してください。
ベンフォークト

おかげで、あなたの役に立つコメントを反映するために私の答えを更新しました。:-)
マットスミジーズ

問題は、セッターメソッドではなく、プロパティセッターについて具体的に尋ねていました。
user253751

確かに、しかし、OPは以下から変更可能な状態を心配している可能性、将来の開発者。private readonlyまたはprivate finalキーワードを使用する変数は自己文書化する必要があります。それが理解されていなければ、それは元の開発者の誤りではありません。状態を変更するさまざまな方法を理解することが重要です。非常に役立つ別のアプローチを追加しました。コードの世界には、過度の妄想を引き起こすものがたくさんあります。プライベートの読み取り専用変数はそれらの1つではありません。
マットスミジーズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.