プライベートセッターを理解する


90

C#2で始まったプライベートセッターの必要性を理解していません。

私のためのセッターメソッドがあると、ユーザーはそのクラスにいくつかの変数を設定できます。その際、変数をユーザーに直接公開しません。代わりに、このパブリックセッターメソッドを使用して許可します。

これは「カプセル化」を使用しています。プライベートセッターを使用するとカプセル化を適用できると主張するいくつかの議論があります。

パブリックセッターメソッドを使用してカプセル化を使用していませんか?なぜプライベートセッターが必要なのですか?

不変クラスとプライベートセッターを持つクラスの違いは何ですか?


1
私はプライベートセッターを大いに愛していました-醜いクラスをリファクタリングするのを助けました。また、非定数のインスタンス変数を次のように一度に宣言して設定することも不可能にprivate File settingsFile = null;なり、さらにコンストラクタの1つでそれを行うこともできなくなりますif (settingsFile == null) { settingsFile = GetSettingsFile() };。そのようなコードをリファクタリングすると、時々私は泣きました:) コンストラクタの前にメンバーを設定できるからといって、複数のコンストラクタを使用する必要があるという意味ではありません。これは、ロジックに従うのが困難になるためです。プライベートセッターは、コンストラクター内または後で値を設定するように強制します。
Hamish Grubijan

回答:


261

論理的に。

プライベートセッターが存在するのは、autoプロパティを使用できるためです。

public int MyProperty { get; set; }

読み取り専用にしたい場合はどうしますか?

public int MyProperty { get; }

やばい!!自分のクラスからはアクセスできません。通常のプロパティのように作成する必要があります。

private int myProperty;
public int MyProperty { get { return myProperty; } }

うーん...しかし、「自動プロパティ」機能を失った...

public int MyProperty { get; private set; }

AHHH ..それは良いです!


ありがとう。これはまた理にかなっている
デーン

3
@ktutnikこれをあなたがした方法でレイアウトしてくれてありがとう。今でも私には理にかなっています!
Vivek M. Chawla 2013年

3
優れた図解の答え。
imnk 2015年

3
Oh crap!! I can't access it from my own classC#6.0以降では、これは初期化フェーズの外でのみ当てはまります。私の回答をご覧くださいstackoverflow.com/a/34223746/198797
tsemer

1
#tsemerの答えにc#6を追加しても、と{get; }同等ではありません{ get; private set; }。最初の方法でproperty.GetSetMethod(true)は戻りnull、後者では戻りますtrue。これには驚きました。
16年

37

プライベートセッターは、読み取り専用プロパティがあり、バッキング変数を明示的に宣言したくない場合に便利です。

そう:

public int MyProperty
{
    get; private set;
}

と同じです:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

自動実装されていないプロパティの場合、クラスからプロパティを設定する一貫した方法を提供するため、検証などが必要な場合に1つの場所しか使用できません。

あなたの最後の質問に答えるために、MSDNはこれをプライベートセッターで言っています:

ただし、値のセット(データ)をカプセル化し、動作がほとんどまたはまったくない小さなクラスまたは構造体の場合、セットアクセサーをプライベートとして宣言して、オブジェクトを不変にすることをお勧めします。

自動実装プロパティのMSDNページから


1
プライベートセッターがあるため、まだ付加価値が表示されません。セッターを公開したくない場合は、ゲッターしかありません。バリデーターを追加したい場合は、パブリックセッターを使用して検証を追加できます。アクセスできないセッターが必要なのはなぜですか?私がそれを理解する方法は、「この車に乗って、それを運転することはできない」のようなものです。どうせ運転しないのであれば、なぜ車を私に与えたいのですか
Dene

@Dene-それは間違いではないので、あなたは確かにそれを行うことができます。プロパティを自動実装することは必須ではありません。
ChrisF

表現の仕方に問題はないことは知っています。C#2による改善を評価するのは私だけです。周りには誇大宣伝がたくさんあるようですが、感じたり、価値を見たりできません。
2010年

@Dene、私はそれについての誇大宣伝のすべてを逃しました。しかし、これが可能であることがようやくわかったとき、.Net 1.1時代の長いクラスをクリーンアップする方法を知っていたので、私は幸せでした。まだ設定されていないプロパティの値を使用することもできますが、nullに設定されているインスタンスメンバー変数の値を使用するよりも、そうするのは自然ではありません。
Hamish Grubijan

コードを簡素化します。自動プロパティと同じように。その後のC#への更新の多くは、コードをより簡潔にして読みやすくすることです。ただし、必要に応じて、他の方法で処理を行うこともできます。下位互換性も大きな目的のようです。それは好みの問題だと思います。
niico 2018

18

かなりシンプルです。プライベートセッターを使用すると、読み取り専用のパブリックプロパティまたは保護プロパティを作成できます。

それでおしまい。それが唯一の理由です。

はい、ゲッターを指定するだけで読み取り専用プロパティを作成できますが、自動実装プロパティではgetとsetの両方を指定する必要があるため、自動実装プロパティを読み取り専用にする場合、プライベートセッター。それを行う他の方法はありません。

プライベートセッターが自動実装された読み取り専用プロパティ用に特別に作成されたわけではないことは事実ですが、他の理由により、その使用はもう少し難解で、主に読み取り専用プロパティとリフレクションとシリアル化の使用に集中しています。


2
「自動実装プロパティを読み取り専用にする場合は、プライベートセッターを使用する必要があります」に感謝します。これは私には理にかなっています
Dene

17

C#6.0の導入とAuto-Property Initializersの構文により、インラインまたはコンストラクター内で初期化時にのみ設定されるプロパティにプライベートセッターは不要になりました。

これらの新しい構文はコンパイルされます。

インライン初期化プロパティ

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

コンストラクタ初期化プロパティ

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@ジグラー、確かにそうではありません。OPも要求しません。彼はそれらを持つ必要性を理解していません。これは答えます:「あなたはもはやこのシナリオでそれらを持つ必要はありません」
tsemer 2018年

6

C#2で始まったプライベートセッターの必要性を理解していません。

たとえば、invoiceクラスでは、ユーザーはItemsプロパティの項目を追加または削除できますが、Items参照を変更することはできません(つまり、ユーザーはItemsプロパティを別の項目リストオブジェクトインスタンスに割り当てることができません)。


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

プロパティがプライベートセッターを持っている場合でも、パブリックゲッターを介して操作できることを強調するための+1。
user1725145 2017年

4

たとえば、プロパティを介して実際の変数を保存したり、値を使用して何かを計算したりしないでください。

そのような場合、計算を行うメソッドを作成することができます

private void Calculate(int value)
{
 //...
}

またはあなたはそれを使ってそうすることができます

public int MyProperty {get; private set;}

これらの場合、プロパティは各メンバー要素をそのままリファクタリングするため、後者を使用することをお勧めします。

それ以外の場合は、プロパティを変数にマップするとも言えます。このような場合、コード内で次のように記述します。

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

プログラマーはGetにMyPropertyを、Setにmypropを常に使用する必要があるため、上記のコードは恐ろしいように見えます。

コード内でそのセッターを使用できる一方で、一貫性を保つために、外部でPropoertyを読み取り専用にするプライベートセッターを使用できます。


3

数人の人がこれについて踊りましたが、私にとって、プライベートセッターの価値は、クラス内でもプロパティの動作をカプセル化できることです。abhishekが述べたように、プロパティが変更されるたびにプロパティ変更イベントを発生させたいが、プロパティをパブリックに読み取り/書き込みしたくない場合は、プライベートセッターを使用するか、バッキングフィールドを変更する任意の場所のイベント。後者は忘れがちなのでエラーが発生しやすいです。関連して、プロパティ値を更新すると、何らかの計算が実行されたり、別のフィールドが変更されたり、何かの遅延初期化が行われたりする場合は、作成した場所すべてで行うことを覚える必要はなく、プライベートセッターでラップすることもできます。バッキングフィールドの使用。


2

カプセル化とは、オブジェクトの状態が定義されたインターフェースを介してのみ発生することを意味します。これにより、クラスはこの状態が常に有効であり、クラスの目的に沿っていることを確認できます。

したがって、場合によっては、フィールドをパブリックに公開することはカプセル化の原則に完全に準拠しています。フィールドのすべての可能な値は、他のすべてのフィールドの可能なすべての値で有効であり、プログラマはフィールドを許可することを積極的に決定できます。外部のコードによって自由に操作されます。

これらのケースは、ほとんどが「単純な古いデータ」であるクラスにほとんど制限されています。彼らはまた、この点であまり興味深いものではないので、それらについては十分です。

他の場合では、他の言語ではint getId()、値を取得しvoid setId(int val)て更新するような、getterおよびsetterメソッドがあります。

プロパティを使用すると、フィールドの読み取りと書き込みに使用するのと同じ構文を、そのようなメソッドを介した読み取りと書き込みに使用できます。これは重要ではありませんが、良い構文上の砂糖です。

(実際には、リフレクションが機能する方法とDataBinder.Eval、フィールドが正常に機能する場合でもプロパティを持つと便利な場合もありますが、それは別の問題です)。

プライベートセッターが導入されるまで(実際には、C#2で変更されたのは、同じブロックにプライベートセッターとパブリックまたは保護されたゲッターを含めるための構文です)、プライベートセッターの処理を実行するプライベートメソッドがありました。プライベートセッターは実際には必要ありません。ただし、これらは便利なので、構文上の砂糖だけですが、かなり便利です。

カプセル化は、セッター(またはゲッター)がパブリック、プライベート、保護、内部のいずれであるかではなく、適切かどうかの問題です。すべてのフィールドのデフォルトがプライベートである(そしてその点についてreadonly)から始め、必要に応じて、それらのフィールドを変更するメンバー(プロパティまたはメソッド)を追加し、オブジェクトが変更されても有効であることを確認します。これにより、クラスの不変条件が維持されます。つまり、クラスが持つことができる有効な状態のセットを記述するルールが破られることはありません(コンストラクターがそのような有効な状態で開始することを確認することによっても役立ちます)。

あなたの最後の質問については、クラスが何のパブリック、保護または内部セッター持っていないことを不変の手段であるとし、無公共、保護または任意のフィールドを変更内部メソッドを。これには次の程度があります。C#では、次の3つの程度が可能です。

  1. クラスのすべてのインスタンスフィールドはreadonlyであるため、プライベートコードでも変更できません。これは不変であることが保証されており(変更しようとしてもコンパイルされません)、これに基づいて最適化を行うことができます。

  2. パブリックメンバーは何も変更しないため、クラスは外部から不変ですがreadonly、内部から変更されないことを保証するものではありません。

  3. 一部の状態は実装の詳細として変更されていますが、クラスは外部から見ると不変です。たとえば、フィールドをメモ化できるため、外部からフィールドを取得しようとすると同じ値が取得されるだけですが、最初のそのような試行では実際にフィールドが計算され、その後の試行で取得できるように格納されます。


1

次のシナリオをサポートする場合は、プライベートセッターが必要です(これだけでなく、これが1つの正当な理由を指摘しているはずです)。クラスに読み取り専用のプロパティがあります。つまり、クラス自体のみ変更できますインスタンスの作成後に変更される場合があります。バインディングの場合は、PropertyChangedイベントを発生させる必要があります。できれば、これは(プライベート)プロパティセッターで行う必要があります。実際には、クラスの他の場所からPropertyChangedイベントを発生させることができますが、このためにプライベートセッターを使用することは、「良い市民権」です。これは、プロパティ変更トリガーをクラス全体に分散せずに、それが属するプロパティ。


1

はい、プロパティを使用してカプセル化を使用していますが、プロパティの読み取りと書き込みの方法を制御するだけではなく、カプセル化の微妙な違いがあります。クラスの外部から設定されるプロパティを拒否すると、堅牢性とパフォーマンスの両方に役立ちます。

不変クラスは、一度作成すると変更されないクラスであるため、プロパティを保護するためにプライベートセッター(またはセッターをまったく使用しない)が必要です。

プライベートセッターは、C#3で導入されたプロパティショートハンドでより頻繁に使用されるようになりました。C#2では、セッターはしばしば省略され、プライベートデータは設定時に直接アクセスされました。

このプロパティ:

public int Size { get; private set; }

と同じです:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

ただし、バッキング変数の名前はコンパイラによって内部的に作成されるため、直接アクセスすることはできません。

簡易プロパティでは、バッキング変数に直接アクセスできないため、読み取り専用プロパティを作成するにはプライベートセッターが必要です。


私が正しくリコール場合は、上のアクセス修飾子異なる持つことができませんでしたgetし、set前にC#2.0に。また、あなたが参照している自動実装の省略表現は3.0だったので、あなたは2.0と3.0を混ぜていると思います。
Anthony Pegram 2010年

1

C#2で始まったプライベートセッターの必要性を理解していません。

使用例:

クラスのコンシューマーに公開したくない'UserInfo'プロパティSessionTokenIDV1を含むアプリケーションオブジェクトのインスタンスがあります。

また、クラスからその値を設定する機能も必要です。

私の解決策は、示されているようにプロパティをカプセル化し、セッターをプライベートにして、インスタンス化コードがそれを設定することを許可せずにセッショントークンの値を設定できるようにすることでした(または私の場合はそれを表示することも)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

編集:コードタグの修正を修正:例には修正されたエラーがありました


-1

https://www.dotnetperls.com/propertyへのクレジット。

プライベートセッターは読み取り専用フィールドと同じです。これらはコンストラクタでのみ設定できます。外部から設定しようとすると、コンパイル時エラーが発生します。

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

クラス外から設定しようとした場合のスクリーンショットをご覧ください。

ここに画像の説明を入力してください

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