Variabile privata vs proprietà?


41

Quando si imposta un valore su una variabile all'interno di una classe il più delle volte ci vengono presentate due opzioni:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Esiste una convenzione che determina come dovremmo assegnare valori alle variabili all'interno delle nostre classi? Ad esempio se ho un metodo all'interno della stessa classe dovrei assegnarlo usando la proprietà o usando la variabile privata. L'ho visto in entrambi i modi, quindi mi chiedevo se questa è una scelta o le prestazioni sono un fattore (minore, probabilmente).

Risposte:


23

Farei un ulteriore passo avanti e lo porterei in 3 casi. Sebbene ci siano variazioni su ciascuno, queste sono le regole che uso la maggior parte delle volte durante la programmazione di C #.

Nel caso 2 e 3, vai sempre all'accessorio proprietà (non alla variabile di campo). E nel caso 1, ti viene salvato anche dal dover fare questa scelta.

1.) Proprietà immutabile (passata al costruttore o creata in fase di costruzione). In questo caso, utilizzo una variabile di campo, con una proprietà di sola lettura. Scelgo questo su un setter privato, poiché un setter privato non garantisce l'immutabilità.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) Variabile POCO. Una semplice variabile che può ottenere / impostare in qualsiasi ambito pubblico / privato. In questo caso userò solo una proprietà automatica.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Proprietà di associazione ViewModel. Per le classi che supportano INotifyPropertyChanged, penso che tu abbia bisogno di una variabile di campo di supporto privata.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}

2
+1 per esempio MVVM. In realtà questo è ciò che ha scatenato la domanda in primo luogo.
Edward,

4
+1: mescola 2/3 con AOP e hai un modo meraviglioso di usare INPC. [Notifica] public int Foo {get; impostato; }
Steven Evers,

1
@Job Per qualsiasi classe che accede alla classe, un setter privato è sufficiente per l'immutabilità. Tuttavia, all'interno della classe, il setter privato non impedisce impostazioni ripetute del valore, dopo la costruzione iniziale. Una caratteristica del linguaggio come un "set privato di sola lettura" potrebbe concettualmente aggirare questo problema, ma non esiste.
Sheldon Warkentin,

1
Sono nuovo in C #, quindi dimmi, perché usare public int Foo {get; set;}invece di public int Foo?

1
Se una classe o una struttura si comporterà come un POCO o PODS, quale reale vantaggio c'è nel racchiudere i campi nelle proprietà? Comprendo appieno che il wrapping dei campi nelle proprietà è utile quando una classe o una struttura ha bisogno, o potrebbe in futuro necessità, di mantenere invarianti rispetto al suo contenuto (forse assicurando che altri oggetti siano aggiornati per abbinarli), ma se una classe o una struttura specifica che i consumatori possono scrivere qualsiasi valore in qualsiasi ordine senza restrizioni o effetti collaterali, quali comportamenti utili potrebbero eventualmente essere aggiunti a un membro accedente?
supercat

18

In generale, direi di assegnare al campo nel costruttore e utilizzare la proprietà ovunque. In questo modo, se qualcuno aggiunge funzionalità alla proprietà, non ti perderai da nessuna parte.

Certamente non è un fattore di prestazione. L'ottimizzatore incorporerà un semplice get o set per te e il codice MSIL finale sarà probabilmente identico.


Qualche motivo particolare per usare il campo nel costruttore? Meno possibilità di strani effetti collaterali?
Firma il

4
@Sign: la mia ipotesi è che se esiste una convalida sulla proprietà (ora o in futuro), non si desidera correre il rischio che la convalida fallisca durante la costruzione. La convalida non è logica in questa fase perché non è possibile garantire che l'oggetto sia stabile fino al termine del costruttore.
Steven Evers,

@Sign: sia quello che hai detto che quello che ha detto Snorfus. O se voglio registrare le modifiche in una proprietà, probabilmente non voglio registrare l'impostazione iniziale. Ma, ho detto "in generale".
pdr,

3
@Sign: il problema è: se il metodo set della proprietà può essere sovrascritto in una sottoclasse, si può avere un effetto collaterale durante la creazione dell'oggetto o di un oggetto incoerente (ovvero: la proprietà sovrascritta è programmata per non impostare alcun valore per quel campo). L'uso delle proprietà nei costruttori è sicuro solo se il metodo set è privato o se la classe è sigillata.
Diego,

4

Dipende.

Innanzitutto dovresti preferire le proprietà automatiche quando possibile:

public string MyValue {get;set;}

Secondo, l'approccio migliore sarebbe probabilmente quello di usare le proprietà, se hai qualche logica lì dovresti probabilmente passarci attraverso da solo, specialmente se quella logica è la sincronizzazione dei thread.

Ma dovresti anche tenere conto del fatto che potrebbe ostacolare le tue prestazioni (un po '), se stai eseguendo una sincronizzazione errata puoi bloccarti e, a volte, il percorso corretto è aggirare la logica nella proprietà.


3
Mi piace anche public string MyValue {get; private set;}.
Lavoro

3

Bene, l'approccio diretto al punto sarebbe quello di assegnarlo alla variabile stessa, poiché sei all'interno di un metodo di classe e controlli comunque il comportamento della classe.

Ma il punto sulle proprietà è che astraggono via la variabile. Mentre una proprietà così semplice come nel tuo esempio non ha alcuna utilità su una semplice variabile membro pubblica, le proprietà di solito (o dovrebbero fare) cose aggiuntive all'interno dei loro getter e setter. E se si desidera che queste cose vengano eseguite automaticamente quando si modifica la proprietà all'interno della classe, è ovviamente più pulito lavorare sulla proprietà anziché sulla variabile per non dover modificare ciascuna assegnazione di variabile quando cambia il comportamento dell'impostazione della proprietà.

Devi solo ragionare su questo concettualmente. La proprietà è in realtà un handle per accedere a uno stato interno dell'oggetto, che può essere composto da più di una variabile membro. Quindi devi chiederti se vuoi cambiare solo lo stato interno sottostante (o solo una parte di esso) o la proprietà astratta che rappresenta questo stato nel suo insieme, e la maggior parte delle volte è davvero quest'ultimo poiché di solito vuoi che il tuo oggetto abbia sempre uno stato coerente.


2

Se esiste la possibilità che tali proprietà ottengano / impostino l'implementazione a volte cambierà in seguito (ad esempio, si desidera generare un evento durante la chiamata seto aggiungere in seguito un meccanismo di valutazione pigro alla propria getfunzione), allora potrebbe essere una buona idea che il codice all'interno della classe utilizzerà la proprietà in quasi tutti i casi, tranne per i casi - molto probabilmente rari - in cui esplicitamente non si desidera utilizzare quegli eventi o meccanismi di valutazione pigri.

Ad ogni modo, qualunque cosa tu faccia, c'è una buona probabilità che quando cambi l'implementazione della proprietà in un secondo momento in questo modo, dovrai guardare tutti i posti all'interno della tua classe accedendo a quella proprietà per verificare se davvero si accede alla proprietà o deve essere utilizzata una variabile privata.


2

Uso sempre la proprietà pubblica.

Spesso una parte della logica che dovrebbe sempre essere eseguita quando la proprietà è impostata viene aggiunta al setmetodo di una proprietà e l'impostazione del campo privato invece il setter pubblico ignorerà qualsiasi logica lì.

Hai un commento su MVVM che porta a questa domanda e ritengo che ciò sia ancora più importante quando si lavora con MVVM. Molti oggetti generano una PropertyChangenotifica al setter e altri oggetti possono iscriversi a questo evento per eseguire alcune azioni quando cambiano proprietà specifiche. Se si imposta la variabile privata, queste azioni non verranno mai eseguite a meno che non venga anche generato manualmente l' PropertyChangedevento.


+1 Sì nella maggior parte dei casi (MVVM) l'evento PropertyChanged è un must. E questo può essere sparato solo all'interno di una proprietà. Buona spiegazione
Edward,

1

In generale, spetta a te cosa dovresti fare con una proprietà e il suo campo di supporto quando ottieni / setta.

Molto spesso, solo per essere coerenti con il codice, è necessario utilizzare gli accessi pubblici ovunque siano disponibili e appropriati. Ciò consente di eseguire il refactoring con una modifica minima del codice; se il metodo che esegue questa impostazione deve essere rimosso dalla classe e messo altrove in cui il campo di supporto non è più disponibile (come una classe base), a chi importa? Stai usando qualcosa che è disponibile ovunque la classe stessa faccia il lavoro. Il campo di supporto, nella maggior parte dei casi, è un dettaglio di implementazione; nessuno al di fuori della tua classe dovrebbe sapere che esiste.

La situazione principale che mi viene in mente quando è necessario utilizzare il campo di supporto e NON l'accessor proprietà è quando l'accessor ha una logica aggiuntiva (convalida o aggiornamento di altre informazioni sullo stato nella classe) che non si desidera eseguire. La popolazione iniziale di un oggetto è un esempio; potresti avere una classe che utilizza due valori di proprietà per calcolare un terzo, anch'esso memorizzato in un campo di supporto (per motivi di persistenza). Quando si inizializza una nuova copia di quell'oggetto dati dati dal DB, gli accessori di proprietà che ricalcolano ciascuno il terzo valore possono lamentarsi se l'altro valore necessario non è impostato. Utilizzando i campi di supporto per impostare i valori iniziali di queste due (o tre) proprietà, si ignora la logica di convalida / calcolo fino a quando l'istanza non si trova in uno stato sufficientemente coerente per il normale funzionamento della logica.


0

Usa sempre quello che ha senso. Sì, lo so che suona piuttosto fasullo al punto da essere una non risposta.

Il punto di proprietà è fornire un'interfaccia attraverso la quale è possibile accedere in sicurezza a un modello di dati. Per la maggior parte delle situazioni si desidera sempre accedere in modo sicuro al modello di dati tramite tale interfaccia, come ad esempio:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Ma in altre situazioni, potresti semplicemente utilizzare una proprietà come vista di un modello di dati:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Se ha senso usare la forma di radianti SomeAngle, allora usala sicuramente.

Alla fine, assicurati di bere il tuo kool-aid. Le API rivolte al pubblico dovrebbero essere abbastanza resistenti da funzionare internamente.

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.