web-dev-qa-db-ja.com

読み取り専用プロパティを実装する方法

タイプに読み取り専用プロパティを実装する必要があります。さらに、このプロパティの値はコンストラクターで設定され、変更されません(WPFのカスタムルーティングUIコマンドを公開するクラスを作成していますが、それは重要ではありません)。

私はそれをする2つの方法を見ます:

  1. class MyClass
    {
        public readonly object MyProperty = new object();
    }
    
  2. class MyClass
    {
        private readonly object my_property = new object();
        public object MyProperty { get { return my_property; } }
    }
    

これらのFxCopエラーはすべて、パブリックメンバー変数を使用すべきではないと言っているので、2番目の方法が正しい方法であると思われます。正しい?

この場合、get onlyプロパティとread onlyメンバーに違いはありますか?

コメント/アドバイス/などをお願いします。

69
akonsu

バージョン管理:
ソースの互換性のみに関心がある場合、それほど違いはないと思います。
プロパティを使用すると、ライブラリに応じてコンパイルされたコードを壊すことなくセッターを持つプロパティに置き換えることができるため、バイナリ互換性が向上します。

コンベンション:
あなたは規約に従っています。このようなケースでは、2つの可能性の違いが慣例に従っていることが比較的小さい方が優れています。あなたに噛み付くかもしれない1つのケースは、リフレクションベースのコードです。たとえば、プロパティエディタ/ビューアなど、フィールドのみを受け入れ、フィールドは受け入れない場合があります。

シリアル化
フィールドからプロパティに変更すると、おそらく多くのシリアライザーが壊れます。 AFAIK XmlSerializerは、パブリックプロパティのみをシリアル化し、パブリックフィールドはシリアル化しません。

自動プロパティの使用
もう1つの一般的なバリエーションは、プライベートセッターで自動プロパティを使用することです。これは短く、プロパティですが、読み取り専用を強制しません。だから私は他のものを好む。

読み取り専用フィールドは自己文書化
ただし、このフィールドには1つの利点があります。
パブリックインターフェイスを一目で見ると、実際には不変であることがわかります(リフレクションを除く)。一方、プロパティの場合は、youしか変更できないため、ドキュメントまたは実装を参照する必要があります。

しかし、正直に言うと、私は怠け者なので、アプリケーションコードで最初のコードを頻繁に使用します。図書館では、私は通常、より徹底的で、規則に従います。

C#6.0は読み取り専用の自動プロパティを追加

public object MyProperty { get; }

したがって、古いコンパイラをサポートする必要がない場合は、読み取り専用フィールドと同じくらい簡潔なコードで、真に読み取り専用のプロパティを持つことができます。

48
CodesInChaos

2番目の方法は推奨オプションです。

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

これにより、MyValは初期化時にのみ割り当てることができます(コンストラクターで設定することもできます)。

既に述べたように、この方法では内部メンバーを公開しないため、将来的に内部実装を変更できます。

58
Oded

C#6(VS 2015)の導入により、暗黙のバッキングフィールドがgetであるreadonlyのみの自動プロパティを使用できるようになりました(つまり、値はコンストラクターで割り当てることができますが、他の場所ではできません) ):

public string Name { get; }

public Customer(string name)  // Constructor
{
    Name = name;
}

private void SomeFunction()
{
    Name = "Something Else";  // Compile-time error
}

また、プロパティを(セッターの有無にかかわらず)インラインで初期化することもできます。

public string Name { get; } = "Boris";

質問に戻ると、これはオプション1の簡潔さでオプション2(パブリックメンバーはフィールドではなくプロパティです)の利点を提供します。

残念ながら、クラスの消費者にとって、セッターがないことはプライベートセッターを持つことと見分けがつかないため、パブリックインターフェイスのレベルでの不変性の保証は提供されません(自己文書化に関する@CodesInChaosのポイントのように)。

41
Bob Sammers

あなたはこれを行うことができます:

public int Property { get { ... } private set { ... } }
11
Nobody

2番目の方法が望ましいことに同意します。その設定の唯一の本当の理由は、.NETクラスにパブリックフィールドがないという一般的な設定です。ただし、そのフィールドが読み取り専用の場合、他のプロパティとの一貫性の欠如以外に実際の異議があるかどうかはわかりません。読み取り専用フィールドと取得専用プロパティの本当の違いは、読み取り専用フィールドは値がオブジェクトの寿命にわたって変化しないことを保証し、取得専用プロパティは変化しないということです。

5
Eric Mickelsen

カプセル化のため、2番目の方法が推奨されます。もちろん、読み取り専用フィールドをパブリックにすることもできますが、これは、フィールドではなくプロパティを介してデータにアクセスするC#のイディオムに反します。

この背後にある理由は、プロパティがパブリックインターフェイスを定義し、そのプロパティのバッキング実装が変更されても、実装がインターフェイスの背後に隠されているため、残りのコードを壊すことはないからです。

4
Joshua Rodgers

c#6から始まる別の方法(私のお気に入り)

private readonly int MyVal = 5;

public int MyProp => MyVal;

https://docs.Microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties#expression-body-definitions

0
owns