C#でプライベートプロパティを使用する理由はありますか?


245

C#のプロパティコンストラクトプライベートアクセス修飾子でも使用できることを認識しました。

private string Password { get; set; }

これは技術的に興味深いものですが、プライベートフィールドで式典が少なくなるため、いつ使用するかは想像できません。

private string _password;

私が今まで内部でできるようにする必要があるだろうというとき、私は想像することはできません取得ではなく、設定セットではなく、取得プライベートフィールド:

private string Password { get; }

または

private string Password { set; }

しかし、おそらく、ネストされたクラスまたは継承されたクラスの使用例があるか、またはプロパティの値を返すだけでなく、get / setにロジックが含まれている可能性があります。例えばGetEncodedPassword()

誰かが何らかの理由でC#でプライベートプロパティを使用していますか、それとも、技術的には可能ですが実際にはほとんど使用されていない実際のコード構成の1つにすぎませんか?

補遺

それらを読んで、私は私有財産のこれらの使用法を選別しました:

  • プライベートフィールドを遅延読み込みする必要がある場合
  • プライベートフィールドが追加のロジックを必要とする場合、または計算された値である場合
  • プライベートフィールドはデバッグが難しいため
  • 「自分に契約を提示する」ため
  • シリアル化の一部として公開されたプロパティを内部的に変換/簡略化する
  • クラス内で使用されるグローバル変数をラップする

プライベートプロパティによって推奨される手法は、自己カプセル化です -参照:sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

回答:


212

値をキャッシュする必要があり、遅延ロードしたい場合は、これらを使用します。

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}

42
これによく使われるパターンはreturn _password ?? (_password = CallExpensiveOperation());
Marc

1
@EvAlex Lazy <T>を使用できる場合は、それを使用することをお勧めします。ただし、静的なものしか使用できないため、(非静的)メソッドや他のメンバーにはアクセスできません。ちなみに、私return _password ?? (_password = CallExpensiveOperation());はしばらくの間、単一行の構文を好みます。または実際にResharperはそれを好みます:)。
バート

4
@MarcはC#6以降、getとreturnを記述する必要さえありません。private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher

1
C#8でそれも短いです:private string Password => _password ??= CallExpensiveOperation();
デイブM

2
@Basこれはレイジーロードではありません。これはCallExpensiveOperation();、含まれているオブジェクトの構築/初期化中に呼び出され、プロパティが最初にアクセスされたときではないためです。
Stefan Podskubka

142

他の人が述べたように、私のコードにおけるこれの主な使用法は遅延初期化です。

フィールドのプライベートプロパティのもう1つの理由は、プライベートプロパティの方がプライベートフィールドよりもはるかに簡単にデバッグできることです。「このフィールドが予期せず設定されます。このフィールドを設定する最初の呼び出し元は誰ですか?」そして、セッターにブレークポイントを置いて、goを打つだけの方が簡単です。そこにロギングを置くことができます。そこにパフォーマンスメトリックを配置できます。デバッグビルドで実行される整合性チェックを入れることができます。

基本的には、コードはデータよりもはるかに強力です。必要なコードを記述できるテクニックはどれも良いものです。フィールドではコードを記述できませんが、プロパティではコードを記述できます。


5
「コードはデータよりもはるかに強力です」というのはあなたの言い分ですか?グーグルすると、あなたを指す参照が返されます。知りたいので、必要なときに正しく引用できるようにします。
Joan Venge

24
@ジョアン:わかりません。私がそれを作ったか、または誰かがそれを言うのを聞いて、「すごい、私はそれを完全に盗んで、それから誰からそれを盗んだかをすべて忘れるべきだ」と思いました。
Eric Lippert

1
+ CodeLensは、プロパティがどこで参照されているかを
通知し

43

おそらく、ネストされたクラスまたは継承されたクラスのユースケースがあるか、またはプロパティの値を返すだけでなく、get / setにロジックが含まれている可能性があります。

プロパティのゲッターまたはセッターのロジックが必要ない場合でも、これを個人的に使用します。プロパティを使用すると、プライベートプロパティでもコードの将来性が保証され、後で必要に応じてロジックをゲッターに追加できます。

プロパティが最終的に追加のロジックを必要とする可能性があると感じた場合は、フィールドを使用する代わりにプライベートプロパティにラップすることもあるので、後でコードを変更する必要はありません。


半関連のケースでは(質問とは異なりますが)、私は頻繁にパブリックプロパティでプライベートセッターを使用します。

public string Password 
{
    get; 
    private set;
}

これにより、ゲッターはパブリックになりますが、セッターはプライベートになります。


+1は意味があります。「プロパティに最終的に追加のロジックが必要になる可能性がある場合は、フィールドを使用する代わりにプライベートプロパティにラップすることもあるので、後でコードを変更する必要はありません。」
Edward Tanguay 2010

7
プライベートセッター<3
Earlz

20

プライベートのget onlyプロパティの1つの適切な使用法は、計算された値です。プライベートな読み取り専用のプロパティが何度かあり、自分のタイプの他のフィールドに対して計算を実行しました。これはメソッドにふさわしくなく、他のクラスには興味がないため、プライベートプロパティです。


20

遅延初期化は、それらがきちんとできる1つの場所です。

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

次に、次のように記述できます。遅延してインスタンス化されるという事実を1つの場所ではthis.MyTypeなく、どこにでもthis.mytype.Valueカプセル化できます。

残念なことの1つは、C#がバッキングフィールドをプロパティにスコープすること(つまり、プロパティ定義内で宣言すること)をサポートして完全に非表示にし、プロパティを介してのみアクセスできることを保証しないことです。


2
そこにスコーピングの側面を持たせることに同意した。
Chris Marisic 2010

5
私はこれと同じ手法を頻繁に使用しています。また、フィールドをコード本体にスコープできるようにしたいと思っています。これは優れた機能ですが、優先度は低くなります。
Eric Lippert、2010

5
@Eric Lippert- field-declarationスコープ内のs accessor-declarationsは、長い間、C#ウィッシュリストで1位にランクされています。(実際の)将来のバージョンで設計および実装できる場合は、簡単に説明します。
Jeffrey L Whitledge、2010

13

私が考えることができる唯一の使用法

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}

他のプライベート変数から計算されるプロパティの有用なクラスの+1
Daren Thomas

2
プライベートメソッドを使用しない理由private bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman

10

プロパティとフィールドは1対1ではありません。プロパティはクラスのインターフェースに関するものであり(そのパブリックインターフェースと内部インターフェースのどちらについて話す場合でも)、フィールドはクラスの実装に関するものです。プロパティは、フィールドを単に公開する方法と見なすべきではなく、クラスの意図と目的を公開する方法と見なすべきです。

プロパティを使用してクラスを構成するものについてコンシューマーにコントラクトを提示するのと同じように、非常に類似した理由でコントラクトを自分に提示することもできます。そう、私はそれが理にかなっているときに私はプライベートプロパティを使用しています。プライベートプロパティは、遅延読み込みのような実装の詳細、プロパティが実際には複数のフィールドとアスペクトの集まりである、またはプロパティを各呼び出しで仮想的にインスタンス化する必要があるという事実を隠すことがあります(と思いますDateTime.Now)。クラスのバックエンドで自分自身にもこれを強制することが理にかなっている場合は間違いありません。


+1:「非常に類似した理由で契約を自分に提示することもできます」
エドワードタングアイ2010

8

私はそれらをシリアライゼーションで使用し、DataContractSerializerまたはprotobuf-netのようなものを使用して、この使用法をサポートしています(サポートXmlSerializerしていません)。シリアル化の一部としてオブジェクトを簡略化する必要がある場合に役立ちます。

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}

6

私がいつもやっていることの1つは、「グローバル」変数/キャッシュを HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}

5

私は時々それらを使用します。プロパティにブレークポイントを簡単に配置したり、ログステートメントを追加したりするときに、デバッグを容易にすることができます。

後で何らかの方法でデータのタイプを変更する必要がある場合や、リフレクションを使用する必要がある場合にも役立ちます。


同上; get / setに関連するロジックがある場合は、プライベートプロパティまたは保護プロパティを使用することがあります。これは一般に、ロジックでどれだけかかるかに依存します。プロパティで実行する単純なロジック、通常は補助関数を使用する多くのロジックです。コードを最も保守しやすくするものは何でも。
TechNeilogy 2010

5

プライベートプロパティを使用して、頻繁に使用するサブプロパティにアクセスするためのコードを削減しています。

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

サブプロパティが多い場合に便利です。


2

get / setメソッドを使用してメンバーを変更することは一般的な方法です。これの背後にあるロジックは、get / setが常に特定の方法(たとえば、イベントの発生)で動作することを知っているためです。これらはプロパティスキームに含まれないため、意味がないようです...しかし、古い習慣はひどく死にます。


2

プロパティのセットまたは取得(遅延初期化を考えてください)に関連付けられたロジックがあり、プロパティがクラスのいくつかの場所で使用されている場合、これは完全に理にかなっています。

それが単なる裏付けの場なら?正当な理由として何も頭に浮かびません。


2

この質問は非常に古くなっていますが、以下の情報は現在の回答には含まれていません。

内部で取得できるが設定できない必要があるときは想像できません

依存関係を注入する場合、読み取り専用のプロパティであることを示すため、セッターではなくプロパティにゲッターを設定することをお勧めします。つまり、プロパティはコンストラクタでのみ設定でき、クラス内の他のコードでは変更できません。

また、Visual Studio Professionalはフィールドではなくプロパティに関する情報を提供し、フィールドが使用されているものを簡単に確認できるようにします。

PorpField


1

まあ、誰も言及しなかったように、それを使用してデータを検証したり、変数をロックしたりできます。

  • 検証

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • ロック

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    注:参照をロックする場合、参照されるオブジェクトのメンバーへのアクセスはロックしません。

遅延参照:遅延読み込みを行うと、非同期で実行する必要が生じることがありますが、現在はAsyncLazyがあります。Visual Studio SDK 2015より古いバージョンを使用しているか、使用していない場合は、AsyncExのAsyncLazyを使用することもできます。


0

明示的なフィールドのエキゾチックな使用法には、次のようなものがあります。

  • あなたが使用する必要があるrefか、outそれはおそらくので、 -の値とInterlockedカウンタ
  • これは、たとえば明示的なレイアウトを持つ基本的なレイアウトを表すことを目的としていstructます(おそらくC ++ダンプまたはunsafeコードにマップするため)。
  • 歴史的にタイプはBinaryFormatter自動フィールド処理で使用されていました(auto-propsに変更すると名前が変更され、シリアライザが壊れます)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.