Best practice per raccoglitori, setter e proprietà. Java contro C #


92

Sto frequentando un corso di C # in questo momento e sto cercando di scoprire il modo migliore di fare le cose. Vengo da un background Java e quindi ho familiarità solo con le best practice Java; Sono un principiante di C #!

In Java, se ho una proprietà privata, lo faccio;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

In C #, vedo che ci sono molti modi per farlo.

Posso farlo come Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Oppure posso farlo in questo modo:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

O:

public string Name { get; set; }

Quale dovrei usare e quali sono gli avvertimenti o le sottigliezze coinvolti in ciascun approccio? Durante la creazione delle classi, seguo le migliori pratiche generali che conosco da Java (in particolare leggendo Java efficace). Quindi, ad esempio, sto favorendo l'immutabilità (fornendo setter solo quando necessario). Sono solo curioso di vedere come queste pratiche si adattano ai vari modi di fornire setter e getter in C #; in sostanza, come tradurre le best practice dal mondo Java in C #?

MODIFICARE

Lo stavo postando come commento alla risposta di Jon Skeet, ma poi è diventato lungo:

Che dire di una proprietà non banale (cioè, forse con elaborazione e convalida significative)? Potrei comunque esporlo tramite una proprietà pubblica ma con la logica incapsulata in gete set? Perché dovrei / dovrei farlo con metodi setter e getter dedicati (con logica di elaborazione e convalida associata).

Risposte:


88

Pre-C # 6

Userei l'ultimo di questi, per una proprietà banale. Nota che la definirei una proprietà pubblica poiché sia ​​i getter che i setter sono pubblici.

L'immutabilità è un po 'un problema con le proprietà implementate automaticamente: non è possibile scrivere una proprietà auto che ha solo un getter; il più vicino che puoi venire è:

public string Foo { get; private set; }

che non è realmente immutabile ... semplicemente immutabile al di fuori della tua classe. Quindi potresti voler usare una vera proprietà di sola lettura invece:

private readonly string foo;
public string Foo { get { return foo; } }

Sicuramente non vuoi scrivere getName()e setName(). In alcuni casi ha senso scrivere metodi Get / Set piuttosto che utilizzare proprietà, in particolare se potrebbero essere costosi e si desidera sottolinearlo. Tuttavia, si vorrebbe seguire la convenzione di denominazione .NET di PascalCase per i metodi e non si vorrebbe comunque che una proprietà banale come questa venga implementata con metodi normali: una proprietà è molto più idiomatica qui.

C # 6

Evviva, finalmente abbiamo proprietà implementate automaticamente di sola lettura appropriate:

// This can only be assigned to within the constructor
public string Foo { get; }

Allo stesso modo per le proprietà di sola lettura che fanno bisogno di fare un po 'di lavoro, è possibile utilizzare le proprietà dei membri di corpo:

public double Area => height * width;

5
Più esatto: qualsiasi revisione del codice indicherà che il metodo Java è un hack che aggira i costrutti di runtime AND (!) Di langauge validi e interrompe l'utilizzo della proprietà come proprietà (cioè object.property = "valore"). In alcune squadre ciò si tradurrebbe in un bel discorso sull'atteggiamento - a seconda dell'anzianità combinata con un incentivo a mettere quell'atteggiamento da usare a un concorrente. Seriamente, NON COMBATTERE LA LINGUA. Soprattutto perché il "modo" java è un hack che è stato scelto per non modificare il linguaggio per il supporto di proprietà immobiliari.
TomTom

1
Immagino che la buona notizia sia che la mia risposta non sembra contraddire nulla di ciò che hai menzionato. La cattiva notizia è che le tue dita sono molto più veloci delle mie. Ottima intuizione e grazie per i dettagli aggiunti.
jeremyalan

4
Per rispondere, modifica: puoi utilizzare i metodi get / set con qualsiasi quantità di logica in essi che desideri. Lo facciamo spesso, soprattutto per la convalida. La migliore pratica, tuttavia, è quella di non avere molta logica lenta (accesso al database ad esempio), logica pericolosa (lancio di eccezioni) o mutazione (modifica di molto stato) nelle proprietà. Ci si aspetta che una proprietà agisca più o meno come uno stato semplice. Qualcosa di più dovrebbe essere indicato utilizzando invece una funzione.
CodexArcanum

2
@rtindru: Sì, ne sono consapevole. È del tutto possibile scrivere una proprietà di sola lettura. Ma non è possibile dichiarare una proprietà implementata automaticamente senza una funzione di accesso set.
Jon Skeet


17

Se tutto ciò di cui hai bisogno è una variabile per memorizzare alcuni dati:

public string Name { get; set; }

Vuoi farlo apparire di sola lettura?

public string Name { get; private set; }

O ancora meglio ...

private readonly string _name;

...

public string Name { get { return _name; } }

Vuoi fare un po 'di controllo del valore prima di assegnare la proprietà?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

In generale, GetXyz () e SetXyz () sono usati solo in alcuni casi, e devi solo usare il tuo istinto quando ti sembra giusto. In generale, direi che mi aspetto che la maggior parte delle proprietà get / set non contenga molta logica e abbia pochissimi effetti collaterali inaspettati. Se la lettura di un valore di proprietà richiede l'invocazione di un servizio o la ricezione di input da un utente per creare l'oggetto che sto richiedendo, lo inserirò in un metodo e lo chiamerei qualcosa di simile BuildXyz(), anziché GetXyz().


2
Non genererei eccezioni nelle proprietà, preferirei usare metodi di settaggio con contratti per specificare un comportamento così specifico. Se una proprietà è di tipo int, mi aspetto che ogni int sia qualificato. Qualcosa che richiede il lancio di eccezioni non è semplice secondo me, le invocazioni di tipo INotifyPropertyChanged sono più in quella linea secondo me.
Flindeberg

3
setter privato! = immutabile
piedar

piedar ha ragione. Un setter privato significa semplicemente che non posso fare assegnazioni, ma posso comunque usarlo myList.Add(), ad esempio (fintanto che l'oggetto è esposto al cambiamento, è mutabile).

1
@flindeberg Scusa ma non sono d'accordo ... E se hai una barra di avanzamento con un valore Min/ Max. Vuoi assicurartelo Max > Min? Avere un SetRange(Min, Max)potrebbe avere senso, ma allora come rileggerai quei valori? Proprietà min / max di sola lettura? Lanciare eccezioni per input non validi sembra il modo più pulito per gestirlo.
Basic

12

Usa le proprietà in C #, non i metodi get / set. Sono lì per la tua comodità ed è idiomatico.

Per quanto riguarda i tuoi due esempi C #, uno è semplicemente zucchero sintattico per l'altro. Usa la proprietà auto se tutto ciò di cui hai bisogno è un semplice wrapper attorno a una variabile di istanza, usa la versione completa quando devi aggiungere logica nel getter e / o setter.


5

In C # privilegiare le proprietà per l'esposizione di campi privati ​​per get e / o set. Il modulo che hai menzionato è un'autoproprietà in cui il get and set genera automaticamente un campo di supporto pivot nascosto per te.

Preferisco le proprietà automatiche quando possibile, ma non dovresti mai fare una coppia di metodi set / get in C #.


5
public string Name { get; set; }

Questa è semplicemente una proprietà implementata automaticamente ed è tecnicamente uguale a una proprietà normale. Durante la compilazione verrà creato un campo di supporto.

Tutte le proprietà vengono infine convertite in funzioni, quindi l'effettiva implementazione compilata alla fine è la stessa a cui sei abituato in Java.

Utilizza le proprietà implementate automaticamente quando non devi eseguire operazioni specifiche sul campo sottostante. Altrimenti usa una proprietà ordinaria. Usa le funzioni get e set quando l'operazione ha effetti collaterali o è computazionalmente costosa, altrimenti usa le proprietà.


4

Indipendentemente dal modo in cui scegli in C #, il risultato finale è lo stesso. Otterrai una variabile di backinng con metodi getter e setter separati. Utilizzando le proprietà stai seguendo le migliori pratiche, quindi è questione di quanto verboso vuoi ottenere.

Personalmente sceglierei le proprietà automatiche, l'ultima versione:, public string Name { get; set; }poiché occupano meno spazio. E puoi sempre ampliarli in futuro se hai bisogno di aggiungere qualcosa come la convalida.


4

Quando possibile, preferisco il pubblico string Name { get; set; }perché è conciso e facilmente leggibile. Tuttavia, ci possono essere momenti in cui questo è necessario

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

2
puoi spiegare quando e perché questo genere di cose è necessario?
Jesper

In questo momento non riesco a pensare a un motivo per utilizzare la seconda versione. Ho solo detto "possono essere momenti in cui ciò è necessario". Odio usare gli assoluti a meno che non sia positivo.
SquidScareMe

3
E perché questo "lolz"? Puoi mostrare il tuo sostegno per un commento votando.
SquidScareMe

1
Un motivo per utilizzare un campo di supporto esplicito è quando si desidera eseguire operazioni atomiche / thread-safe rispetto al valore
Basic

4

In C # il modo preferito è attraverso proprietà anziché getX()e setX()metodi. Inoltre, si noti che C # non richiede che le proprietà abbiano sia un get che un set: è possibile avere proprietà di solo get e proprietà di solo set.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}

4

Per prima cosa fammi provare a spiegare cosa hai scritto:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Questa struttura viene utilizzata anche al giorno d'oggi ma è più adatta se si desidera eseguire alcune funzionalità extra, ad esempio quando è impostato un valore è possibile analizzarlo per capitalizzarlo e salvarlo in un membro privato per alterarne l'uso interno.

Con .net framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Ti auguro buona fortuna in C #


3

Come accennato, tutti questi approcci portano allo stesso risultato. La cosa più importante è scegliere una convenzione e mantenerla. Preferisco usare gli ultimi due esempi di proprietà.


2

come la maggior parte delle risposte qui, usa le proprietà automatiche. Intuitivo, meno righe di codice ed è più pulito. Se devi serializzare la tua classe, contrassegna la classe [Serializable]/ con l' [DataConract]attributo. E se stai usando [DataContract]contrassegna il membro con

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Il setter privato o pubblico dipende dalle tue preferenze.

Si noti inoltre che le proprietà automatiche richiedono sia getter che setter (pubblici o privati).

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

In alternativa, se desideri solo ottenere / impostare, crea tu stesso un campo di supporto


0

Quale dovrei usare e quali sono gli avvertimenti o le sottigliezze coinvolti in ciascun approccio?

Quando si va con le proprietà c'è un avvertimento che non è stato ancora menzionato: con le proprietà non è possibile avere alcuna parametrizzazione dei propri getter o setter.

Ad esempio, immagina di voler recuperare una voce di elenco e di voler applicare anche un filtro allo stesso tempo. Con un metodo get potresti scrivere qualcosa come:

obj.getItems(filter);

Al contrario, con una proprietà sei costretto a restituire prima tutti gli articoli

obj.items

e quindi applica il filtro nel passaggio successivo o devi aggiungere proprietà dedicate che espongono elementi filtrati in base a criteri diversi, che presto gonfiano la tua API:

obj.itemsFilteredByX
obj.itemsFilteredByY

Quello che a volte può essere un fastidio è quando hai iniziato con una proprietà, ad esempio obj.itemse poi hai scoperto che è necessaria la parametrizzazione getter o setter o che renderebbe le cose più facili per l'utente API di classe. Ora dovresti riscrivere la tua API e modificare tutti quei punti nel tuo codice che accedono a questa proprietà o trovare una soluzione alternativa. Al contrario, con un metodo get, ad esempio obj.getItems(), puoi semplicemente estendere la firma del tuo metodo per accettare un oggetto di "configurazione" opzionale, ad esempio obj.getItems(options)senza dover riscrivere tutti quei posti che chiamano il tuo metodo.

Detto questo, le proprietà (implementate automaticamente) in C # sono ancora scorciatoie molto utili (per i vari motivi qui menzionati) poiché la maggior parte delle volte la parametrizzazione potrebbe non essere necessaria, ma questo avvertimento è valido.

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.