web-dev-qa-db-ja.com

読み取り専用修飾子とプライベートセッターのどちらが優れていますか?

私はクラスの作成に取り組んでいて、突然、2つのコードの違いは何なのかと思いました。

public readonly string ProductLocation;

そして

public string ProductLocation
{
     get;
     private set;
}

次のものをいつ使うべきかについて教えてください。ありがとう。

61
Allan Chua

最初のフィールドは読み取り専用フィールドですが、2番目のフィールドはメソッドのペアとしてコンパイルされます(そして、プロパティProductLocationのすべての読み取りは、対応するgetメソッドの呼び出しにコンパイルされて書き込みますsetメソッドへの呼び出しにコンパイルされます。内部では、これらのメソッドは内部の自動生成された非読み取り専用フィールドから読み取り/書き込みを行います)。 最も重要な違いはスレッドの安全性です! (どうやって?読んでください!)

クラスの基本的な使用法はまったく同じに見えます。他のクラスのコードは値を読み取ることだけができ、変更はできません。また、値を読み取るコードはまったく同じように見えます(たとえば、print(myInstace.ProductLocation);ここでは、それがどのように宣言されているかわかりません、かっこいい、えっ?)

最初の最も重要な違いは、プライベートセッターを使用したプロパティでは、同じクラスのインスタンスが値を変更できることです。一方、読み取り専用プロパティの場合、オブジェクト自体でも値を変更できません。

さて、スレッドセーフのために。フィールドのreadonly属性は、複数のスレッドで作業しているときに、そのメモリの可視性のセマンティクスを変更します。 (Javaのfinalフィールドと同じように)。

readonlyフィールドは、宣言時またはコンストラクターでのみ割り当てることができます。 readonlyフィールドに割り当てられた値は変更できません(少なくとも通常の方法では変更できません)。コンストラクターが戻った後、すべてのスレッドは正しく初期化された値を参照します。したがって、readonlyフィールドは本質的にスレッドセーフです。

プロパティで同じスレッドセーフを実現するには、コードに同期を追加する必要があり、エラーが発生しやすくなります。場合によっては、特に経験がない場合、デッドロック、データ競合、またはパフォーマンスの低下につながる可能性があります。

したがって、値がオブジェクトの構築後に意味的に変更できないものを表す場合は、プライベートセッターを宣言しないでください(オブジェクトが変更する可能性があることを意味します)。読み取り専用フィールドに移動します(おそらくフィールドをプライベートに宣言し、フィールドにアクセスするゲッターのみでパブリックプロパティを宣言します!これは実際に推奨されるフォームです。フィールドを公開するのはよくないので、メソッドのみを公開する方が良いです-そこ この答え )で理由を説明する多くの理由があります

71
Bruno Reis

C#6.auto-property initializerを使用すると、ボイラープレートの実行方法が少なくなります

private readonly string productLocation; 
public string ProductLocation { get { return productLocation; } } 

それは

public string ProductLocation { get; } 

これは読み取り専用です。コンストラクターまたはインラインからのみ初期化されます。初期化後は編集できません。 (どこからでも変更不可)

ただし、プライベートセットを使用する場合。

public string ProductLocation { get; private set } 

これは外部からは読み取り専用です。ただし、クラス自体の任意の場所でいつでも初期化できます。また、クラス自体によってライフサイクル内で編集できます。 (クラスから変更可能、外部から変更不可)

23
stratovarius

一般に、.NETでメンバーフィールドを公開することはお勧めしません。メンバーフィールドはプロパティでラップする必要があります。だからあなたが持っているかもしれないと仮定しましょう

private readonly string productLocation; 
public string ProductLocation { get { return productLocation; } } 

public string ProductLocation { get; private set; }

このセットアップでは、リフレクションによって達成できることを無視すると、セマンティクスは、最初のケースでは、productLocation変数は所定の場所およびクラスコンストラクターでのみ初期化できることです。クラスの他のメンバーは値を変更できません。外部の消費者は値を設定することができません。

2番目のバージョンでは、外部のコンシューマーは値の設定にアクセスできません。ただし、クラス自体はいつでも値を変更できます。持っているのがDTO(つまり、データを転送するだけのクラスであり、メソッドを介してロジックが表現されていない)である場合、これは基本的にreadonlyバージョンとそれほど変わらないわけではありません。ただし、メソッドを持つクラスの場合、それらのメソッドはProductLocationの背後の値を変更する可能性があります。

不変フィールドの構築後の概念を適用する場合は、readonlyを使用します。しかし、DTOの場合、private set;オプション、これは主に定型コードが少ないためです。

18
Anthony Pegram

最初の(readonlyを使用)は、オブジェクトがインスタンス化されると、オブジェクト自身のフィールドの値を変更することもできませんを意味し、他のユーザーはオブジェクトを変更できません。

2つ目(private setを使用)は、オブジェクトcanがインスタンス化された後にそのフィールドの値を変更しますが、他のユーザーが変更することはできません。

私はあなたが知っている何かに前者を使用します変更されません、そして値が変更されるかもしれないが他の人にそれを変更させたくない場合に後者を使用します。

8
Jon Newmuis

最初はfieldで、その値はインスタンス化時のみに設定できます。

2番目はプロパティで、その値はいつでもに設定できます(ただし、それを含むオブジェクトによってのみ)。


修正:プロパティは、同じクラスの任意のインスタンス(いつでも、それを含むオブジェクトだけでなく)によっていつでも設定できます。

4
Kirk Broadhurst