プライベート変数とプロパティ?


41

ほとんどの場合、クラス内の変数に値を設定すると、次の2つのオプションが表示されます。

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

クラス内の変数に値を割り当てる方法を決定する規則はありますか?たとえば、同じクラス内にメソッドがある場合、プロパティまたはプライベート変数を使用して割り当てる必要があります。私はそれが両方の方法で行われたことを見てきましたので、これが選択肢なのかパフォーマンスが要因なのか(おそらく、おそらく)疑問に思っていました。

回答:


23

さらに一歩進んで、3つのケースに持ち込みます。それぞれにバリエーションがありますが、これは私がC#プログラミングの際にほとんどの場合に使用するルールです。

ケース2と3の場合は、常にプロパティアクセサーに移動します(フィールド変数ではありません)。ケース1では、この選択をしなくても済むようになります。

1.)不変のプロパティ(コンストラクターに渡されるか、構築時に作成される)。この場合、読み取り専用プロパティを持つフィールド変数を使用します。プライベートセッターは不変性を保証しないため、プライベートセッターよりもこれを選択します。

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.)POCO変数。任意のパブリック/プライベートスコープで取得/設定できる単純な変数。この場合、自動プロパティを使用します。

public class Abc
{ 
  public int Foo {get; set;}
}

3.)ViewModelバインディングプロパティ。INotifyPropertyChangedをサポートするクラスの場合、プライベートのバッキングフィールド変数が必要だと思います。

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}

2
MVVMの例では+1。実際、それがそもそも疑問を引き起こしたものです。
エドワード

4
+1:AOPと2/3を混ぜると、INPCを使用する素晴らしい方法があります。[通知] public int Foo {get; セットする; }
スティーブンエバーズ

1
@Jobクラスにアクセスするクラスには、不変のプライベートセッターで十分です。ただし、クラス内では、プライベートセッターは、初期構築後の値の設定の繰り返しを防止しません。「プライベート読み取り専用セット」のような言語機能は、この問題を概念的に回避するかもしれませんが、存在しません。
シェルドンウォーケンティン

1
私はそう教えて、C#に新たなんだ、なぜ使用public int Foo {get; set;}の代わりにpublic int Foo

1
クラスまたは構造体がPOCOまたはPODSとして動作する場合、プロパティでフィールドをラップすることの本当の利点は何ですか?クラスまたは構造体がその内容に関して不変条件を維持する必要がある場合、または将来必要になる場合(おそらく他のオブジェクトがそれらに一致するように更新されることにより)、プロパティでフィールドをラップすることが有用であることを完全に理解していますが、クラスまたは構造体消費者が制限や副作用なしに任意の順序で任意の値を書き込むことができることを指定します。メンバーアクセサーに追加できる有用な動作は何ですか?
-supercat

18

一般に、コンストラクターのフィールドに割り当て、他のすべての場所でプロパティを使用すると言います。このように、誰かがプロパティに機能を追加しても、どこでも見逃すことはありません。

それは確かにパフォーマンス要因ではありません。オプティマイザーは単純なgetまたはsetをインライン化し、最終的なMSILコードはおそらく同一になります。


コンストラクタでフィールドを使用する特別な理由はありますか?奇妙な副作用の可能性が少ない?
サイン

4
@Sign:私の推測では、プロパティに検証がある場合(現在または将来)、構築中に検証が失敗するリスクを実行する必要はありません。この段階では、コンストラクターが終了するまでオブジェクトの安定性を保証できないため、検証は論理的ではありません。
スティーブンエバーズ

@Sign:あなたが言ったこととSnorfusが言ったことの両方。または、プロパティへの変更をログに記録する場合、初期設定をログに記録したくないと思われます。しかし、私は「一般的に」と言いました。
pdr

3
@Sign:問題は、サブクラスでプロパティのsetメソッドをオーバーライドできる場合、オブジェクトまたは矛盾するオブジェクトの作成中に副作用が発生する可能性がある(つまり、オーバーライドされたプロパティが値を設定しないようにプログラムされているそのフィールド)。コンストラクターでプロパティを使用することは、setメソッドがprivateであるか、クラスがシールされている場合にのみ安全です。
ディエゴ

4

依存します。

まず、可能な場合は自動プロパティを優先する必要があります。

public string MyValue {get;set;}

第二に、より良いアプローチはおそらくプロパティを使用することです。そこに何らかのロジックがある場合、特にそのロジックがスレッド同期である場合は、おそらく自分でそれを渡す必要があります。

ただし、パフォーマンスを低下させる可能性があることも考慮する必要があります(少し)。誤って同期している場合は、デッドロックする可能性があり、適切なパスはプロパティのロジックを回避することです。


3
私も好きpublic string MyValue {get; private set;}です。
仕事

3

さて、あなたはクラスのメソッドの中にいて、とにかくクラスの振る舞いを制御するので、まっすぐにポイントするアプローチは変数自体にそれを割り当てることです。

しかし、プロパティに関するポイントは、変数を抽象化することです。あなたの例のような単純なプロパティは、単純なパブリックメンバ変数だけではまったく使い物になりませんが、プロパティは通常、ゲッターとセッター内で追加のことを行います(または行う必要があります)。また、クラス内のプロパティを変更するときにこれらのことを自動的に実行したい場合、プロパティ設定の動作が変更されたときに各変数の割り当てを変更する必要がないように、変数の代わりにプロパティで作業する方がもちろんきれいです。

あなたはそれについて概念的に推論する必要があります。このプロパティは、実際にはオブジェクトの内部状態にアクセスするためのハンドルであり、複数のメンバー変数で構成される場合があります。したがって、基礎となる内部状態のみ(またはその一部のみ)を変更するか、この状態を全体的に表す抽象プロパティを変更するかを自問する必要があります。通常、オブジェクトには常に一貫した状態。


2

これらのプロパティの取得/設定の実装が後で変更される可能性がある場合(たとえば、を呼び出すときにイベントを発生させたい場合setや、後で遅延評価メカニズムをget関数に追加する場合)、それは良い考えですクラス内のコードは、これらのイベントまたは遅延評価メカニズムを使用することを明示的に望まない場合を除き、ほとんどの場合、ほとんどすべての場合にプロパティを使用します。

とにかく、あなたが何をするにしても、後でそのような方法でプロパティの実装を変更するときは、実際にプロパティにアクセスするかどうかを確認するためにそれらのプロパティにアクセスするクラス内のすべての場所を見る必要がありますプライベート変数が使用されます。


2

私は常にパブリックプロパティを使用します。

多くの場合、プロパティの設定時に常に実行する必要のあるロジックがプロパティのsetメソッドに追加され、代わりにパブリックフィールドを設定すると、パブリックセッターはそこでロジックをバイパスします。

この質問につながるMVVMについてのコメントがあります。MVVMを使用する場合、これはさらに重要だと感じています。多くのオブジェクトPropertyChangeはセッターに通知を発行し、他のオブジェクトはこのイベントにサブスクライブして、特定のプロパティが変更されたときにアクションを実行できます。プライベート変数を設定すると、これらのアクションは手動でPropertyChangedイベントを発生させない限り実行されません。


+1はい、ほとんどの場合(MVVM)、PropertyChangedイベントは必須です。そして、それはプロパティ内でのみ発生します。良い説明。
エドワード

1

一般的に、取得/設定時にプロパティとそのバッキングフィールドをどのように処理するかはユーザー次第です。

ほとんどの場合、コード間で一貫性を保つために、利用可能な適切な場所であればどこでもパブリックアクセサーを使用する必要があります。これにより、最小限のコード変更でリファクタリングできます。この設定を行うメソッドをクラスから取り除き、バッキングフィールドが使用できなくなった場所(基本クラスなど)に配置する必要がある場合、誰が気にしますか?クラス自体が仕事をする場所であればどこでも利用可能なものを使用しています。ほとんどの場合、バッキングフィールドは実装の詳細です。あなたのクラスの外の誰もそれが存在することを知らないはずです。

プロパティアクセサーではなく、バッキングフィールドを使用する必要がある主な状況は、アクセサーに実行したくない追加のロジック(検証、またはクラス内の他の状態情報の更新)がある場合です。オブジェクトの初期設定は一例です。2つのプロパティ値を使用して3番目の値を計算するクラスがあり、これも(永続化のため)バッキングフィールドに格納されます。DBからのデータを指定してそのオブジェクトの新しいコピーを初期化するとき、他の必要な値が設定されていない場合、それぞれ3番目の値を再計算するプロパティアクセサーが文句を言うことがあります。バッキングフィールドを使用してこれら2つ(または3つ)のプロパティの初期値を設定することにより、インスタンスがロジックが正常に動作するのに十分な一貫した状態になるまで、検証/計算ロジックをバイパスします。


0

常に意味のあるものを使用してください。はい、私はそれが無回答であるという点でかなり偽りに聞こえることを知っています。

プロパティのポイントは、データモデルに安全にアクセスできるインターフェイスを提供することです。ほとんどの場合、次のようなインターフェイスを介してデータモデルに安全にアクセスする必要があります。

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

しかし、他の状況では、単にデータモデルのビューとしてプロパティを使用している場合があります。

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

のラジアン形式を使用することが理にかなっている場合はSomeAngle、必ず使用してください。

最後に、あなた自身のクールエイドを飲むようにしてください。一般向けAPIは、内部で動作するのに十分な回復力を備えている必要があります。

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