web-dev-qa-db-ja.com

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

プロパティでプライベートセットを使用する必要があるのは、読み取り専用プロパティにする場合ですか。以下の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);
    }
}

どちらも同じ結果になります。これは正しいことも悪いこともなく、ただ好みの問題なのでしょうか?

48
tgxiii

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

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

public string Name { get; private set; }   

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

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

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

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

43
Adam Lear

必要な場合はプライベートセットを使用してくださいセッターは外部からアクセスできません

プロパティを一度だけ設定するにする場合は、readonlyを使用します。コンストラクターまたは変数初期化子内。

これをテスト:

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
    }
}
31
asakura89

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を使用するよりも、明示的なSetを使用することをお勧めします。通常、setプロパティ値の場合、後でgetを呼び出すと同じ値が返されることが予想されますが、ToTitleCaseを- セット

しかし、あなたが言ったように、正解は1つではありません。

9
Dave Wise

C#6.0以降、ゲッターのみの自動プロパティが言語に追加されました。ここを参照してください: https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#getter-only-auto-properties

次に例を示します。

public class SomeClass
{
    public int GetOnlyInt { get; }

    public int GetOnlyIntWithInitializer { get; } = 25;

    public SomeClass(int getOnlyInt)
    {
        GetOnlyInt = getOnlyInt;
    }
}
5
Daniel Neel

2番目の例は使用しないでください。プロパティを使用することの要点は、ゲッターの取得とセッターの設定以外に何も起こらない場合でも、そのゲッターとセッターを介してすべてのアクセスを集中させることです。これにより、今後動作を変更する必要がある場合は、すべてが揃います。一箇所。

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

4
Carson63000

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

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

2
Prlaba

そして、実質的にパフォーマンスの低下はありません...

しかし、明確にするために、プロパティへのアクセスisは、バッキング変数へのアクセスよりも低速です。プロパティのバッター変数に直接アクセスするのに対し、プロパティのゲッターとセッターは、呼び出しと戻りを必要とするメソッドです。

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

0
Prlaba