web-dev-qa-db-ja.com

不変フィールドのセッターをどのように処理しますか?

私は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. :-(

assumingに起因するバグは、確実に油断できません。もちろん、間違った開発者はドキュメントを読んだはずです。しかし、コンパイルエラーや実行時エラーが問題について警告しなかったという事実は変わりません。

開発者が上記の間違いを犯す可能性を低くするために、Thingクラスをどのように変更する必要がありますか?

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

25
kdbanman

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

public int Foo { get { return _foo; } }
106
paparazzo

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行は(堅牢な読み取り専用要件を実現する)コンストラクターでのみ有効であり、バッキングフィールドは暗黙的に定義されます(より単純なコードを実現します)。

44
David Arno

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

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; }
}
12
David Arno

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 static factory

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...
}
6
Matt Smithies