.NETプロパティ-プライベートセットまたは読み取り専用プロパティを使用しますか?


45

プロパティでプライベートセットを使用し、ReadOnlyプロパティにする場合はどのような状況ですか?以下の非常に単純な2つの例を考慮してください。

最初の例:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

2番目の例:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

どちらも同じ結果をもたらします。これは善悪の状況ではなく、単に好みの問題ですか?


public string Name { get; protected set; }継承を通じて。
-samis

回答:


42

使用する理由はいくつかありますprivate set

1)バッキングフィールドをまったく使用せず、読み取り専用の自動プロパティが必要な場合:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2)クラス内の変数を変更するときに追加の作業を行い、それを単一の場所でキャプチャする場合:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

ただし、一般的には、個人的な好みの問題です。私が知っている限りでは、どちらかを使用するパフォーマンス上の理由はありません。


1
質問にもvb.netタグがあるため、これを追加するだけですが、vb.netでは、getまたはsetのいずれかでprivateを使用する場合、バッカーを指定する必要があります。そのため、vb.netでは、プロパティを読み取り専用にすることは実際にはあまり手間がかかりません。
user643192

私はそれを知らなかったprivate set。:-)
アフザールアフマドジーシャン14

9
2016年にこの回答を読んだ人向けの更新。C#6.0では、読み取り専用の自動プロパティが導入されました。これにより、バッキングフィールドなしで読み取り専用のプロパティを持つことができますpublic string Name { get; }。変更可能なプロパティが必要ない場合は、これが好ましい構文です。
アレクセイ

4
使用しない非常に良い理由の1つprivate setは、ふりをするのが好きなほど不変ではないということです。真に不変のクラスを実装する場合は、読み取り専用が必須です。
ラバーダック16

読み取り専用を使用しないことがパフォーマンス上の理由になる場合があります。読み取り専用の構造体フィールドのメソッドにアクセスするときに、構造体の不必要なコピーを引き起こすようです。codeblog.jonskeet.uk/2014/07/16/...
Triynko

28

外部からセッターにアクセスできないようにする場合は、プライベートセットを使用します。

プロパティを1回だけ設定する場合は、読み取り専用を使用します。コンストラクターまたは変数初期化子。

これをテスト:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}

私はあなたの答えに同意しますが、あなたの例はいくらか改善することができます。コンパイラエラーが発生する場所にコメントを付けることができます。
マイケルリチャードソン14

NB C#readonlyキーワードに対応するVB.NET構文はReadOnly、プロパティではなくフィールドに適用されます。
ゼフスピッツ

8

3番目のオプションを提案できますか?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

これにより、Nameプロパティがすべての外部コードに対して事実上読み取り専用になり、明示的なSetメソッドが提供されます。設定時に値を変更するため、Nameプロパティのセットを単に使用するよりも、明示的なSetの方が好きです。通常、プロパティ値を設定すると、後でgetを呼び出したときに同じ値が返されることが期待されます。これは、setで ToTitleCaseを実行した場合には発生しません。

しかし、あなたが言ったように、正しい答えはありません。


「プライベートセット」には、コンパイラで特別なセマンティクスがあります(プライベートアクセサとして実行するだけではありません)。これは、保護セットの場合でもありますか?そうでない場合、プライベートセットに特別なセマンティクスがある場合、保護セットと同等のセマンティクスはどこにありますか?これを説明するドキュメントを見つけることができませんでした。
スプレーグ

1
+1ですが、「SetName」ではなく「Rename」メソッドを呼び出します。
MattDavey


4

2番目の例を使用しないでください。プロパティを使用する全体のポイントは、ゲッターの取得とセッターの設定以外に何も起こっていない場合でも、そのゲッターとセッターを介してすべてのアクセスを集中させることです。一箇所。

2番目の例は、プロパティを設定する場合にそれを放棄します。大規模で複雑なクラスでそのアプローチを使用し、後でプロパティの動作を変更する必要がある場合は、1つの場所(プライベートセッター)で変更を行うのではなく、検索と置換の土地にいることになります。


2

セッターのアクセスレベルを変更する必要があるたびに、通常、Protected(このクラスと派生クラスのみが値を変更できます)またはFriend(アセンブリのメンバーのみが値を変更できます)に変更しました。

ただし、セッターでバッキング値を変更する以外のタスクを実行する場合は、Privateを使用するのが最適です。先に指摘したように、バッキング値を直接参照せず、代わりにそれらのプロパティを介してのみアクセスすることをお勧めします。これにより、後でプロパティに加えた変更が、内部だけでなく外部にも適用されます。また、プロパティとそのバッキング変数を参照しても、パフォーマンスの低下はほとんどありません。


0

また、パフォーマンスの低下はほとんどありません...

しかし、明確にするために、プロパティにアクセスすること、その裏変数にアクセスするよりも遅いです。プロパティのgetterおよびsetterはCallおよびReturnを必要とするメソッドですが、プロパティのバッキング変数は直接アクセスされます。

そのため、プロパティのgetterがコードブロック内で何度もアクセスされる可能性がある場合、プロパティの値が最初にキャッシュされ(ローカル変数に保存され)、代わりにローカル変数が使用されることがあります。もちろん、ブロックの実行中にプロパティを非同期に変更できないことを前提としています。

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