Proprietà .NET: utilizzare la proprietà Set privato o ReadOnly?


45

In quale situazione dovrei usare un set privato su una proprietà anziché renderlo una proprietà ReadOnly? Prendi in considerazione i due esempi molto semplicistici di seguito.

Primo esempio:

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);
    }
}

Secondo esempio:

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);
    }
}

Entrambi danno gli stessi risultati. È una situazione in cui non c'è giusto o sbagliato, ed è solo una questione di preferenza?


public string Name { get; protected set; }attraverso l'eredità.
samis,

Risposte:


42

Ci sono un paio di motivi per usare private set.

1) Se non si utilizza affatto un campo di supporto e si desidera una proprietà automatica di sola lettura:

public string Name { get; private set; }   

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

2) Se vuoi fare un lavoro extra quando modifichi la variabile all'interno della tua classe e vuoi catturarla in un'unica posizione:

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

In generale, tuttavia, è una questione di preferenze personali. Per quanto ne so, non ci sono motivi di performance da usare l'uno sull'altro.


1
Basta aggiungere questo perché la domanda ha anche un tag vb.net, ma in vb.net è necessario specificare un backer se si utilizza privato su get o set. Quindi in vb.net è in realtà meno lavoro per rendere la proprietà di sola lettura penso.
user643192,

Non l'ho mai saputo private set. :-)
Afzaal Ahmad Zeeshan,

10
Un aggiornamento per coloro che leggono questa risposta nel 2016. C # 6.0 ha introdotto sola lettura auto-proprietà, che consentono di avere una proprietà di sola lettura senza un campo supporto: public string Name { get; }. Se non si desidera una proprietà modificabile, questa è la sintassi preferita ora.
Alexey,

4
Un ottimo motivo per non usarlo private setè che non è così immutabile come vorremmo fingere. Se vuoi implementare una classe veramente immutabile, la sola lettura è d'obbligo.
RubberDuck,

Potrebbe essere un motivo di prestazione da NON utilizzare in sola lettura. Sembra causare la copia non necessaria di strutture quando si accede ai metodi di un campo di sola lettura. codeblog.jonskeet.uk/2014/07/16/…
Triynko

28

Utilizzare il set privato quando si desidera che il setter non sia accessibile dall'esterno .

Utilizzare di sola lettura quando si desidera impostare la proprietà una sola volta . Nel costruttore o inizializzatore variabile.

PROVA QUESTO:

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

Anche se sono d'accordo con la tua risposta, il tuo esempio potrebbe utilizzare alcuni miglioramenti. Potresti voler fare un commento in cui si verificherebbe l'errore del compilatore.
Michael Richardson,

NB La sintassi VB.NET corrispondente alla readonlyparola chiave C # , deve essere applicata ReadOnlyal campo anziché alla proprietà.
Zev Spitz

8

Potrei suggerire una terza opzione?

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);
    }
}

Ciò rende efficacemente la proprietà Nome Sola lettura a tutto il codice esterno e fornisce un metodo Set esplicito. Preferisco il Set esplicito piuttosto che semplicemente utilizzare il set sulla proprietà Name perché stai modificando il valore quando lo imposti. Normalmente se si imposta un valore di proprietà, ci si aspetta di recuperare lo stesso valore quando si chiama get in seguito, cosa che non accadrebbe se si eseguisse ToTitleCase nel set .

Tuttavia, come hai detto, non esiste una risposta giusta.


Credo che il 'set privato' abbia una semantica speciale nel compilatore (non semplicemente esibendosi come un accessorio privato). È anche il caso di un set protetto? In caso contrario, dov'è l'equivalente semantico del set protetto se il set privato ha una semantica speciale? Non sono stato in grado di trovare alcuna documentazione che spieghi questo.
Sprague

1
+1 ma chiamerei il metodo "Rinomina" anziché "SetName".
MattDavey,


4

Non usare il secondo esempio. Il punto centrale dell'uso di una proprietà - anche se non c'è nulla al di là del recupero del getter e dell'impostazione del setter - è incanalare tutti gli accessi attraverso quel getter e setter in modo che se dovessi mai cambiare comportamento in futuro, è tutto in un posto.

Il tuo secondo esempio abbandona quello nel caso dell'impostazione della proprietà. Se avessi usato quell'approccio in una classe ampia e complessa e in seguito avessi bisogno di cambiare il comportamento della proprietà, saresti nella terra di ricerca e sostituzione, invece di apportare la modifica in un unico posto: il setter privato.


2

Ogni volta che ho bisogno di cambiare il livello di accesso di un setter, generalmente l'ho cambiato in Protetto (solo questa classe e le classi derivate possono cambiare il valore) o Amico (solo i membri del mio assembly possono cambiare il valore).

Ma utilizzare Private ha perfettamente senso quando si desidera svolgere altre attività nel setter oltre a modificare il valore di supporto. Come sottolineato in precedenza, è buona norma non fare riferimento direttamente ai valori di supporto, ma accedervi solo attraverso le loro proprietà. Ciò garantisce che le modifiche successive apportate a una proprietà vengano applicate sia internamente che esternamente. E non c'è praticamente alcuna penalità prestazionale nel fare riferimento a una proprietà rispetto alla sua variabile di supporto.


0

E praticamente non c'è penalità per le prestazioni ...

Ma per chiarire, l'accesso a una proprietà è più lento dell'accesso alla sua variabile di supporto. Il getter e il setter di una proprietà sono metodi che richiedono una chiamata e un ritorno, mentre si accede direttamente alla variabile di supporto di una proprietà.

Ecco perché, nei casi in cui è possibile accedere più volte al getter di una proprietà all'interno di un blocco di codice, a volte il valore della proprietà viene prima memorizzato nella cache (salvato in una variabile locale) e viene invece utilizzata la variabile locale. Naturalmente, ciò presuppone che la proprietà non possa essere modificata in modo asincrono mentre il blocco è in esecuzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.