Getter e setter implementati automaticamente rispetto ai campi pubblici

Vedo un sacco di codice di esempio per le classi C # che fa questo:

public class Point { public int x { get; set; } public int y { get; set; } } 

Oppure, nel codice precedente, lo stesso con un valore di backing privato esplicito e senza le nuove proprietà autoattive:

 public class Point { private int _x; private int _y; public int x { get { return _x; } set { _x = value; } } public int y { get { return _y; } set { _y = value; } } } 

La mia domanda è perché. C’è qualche differenza funzionale tra fare quanto sopra e solo rendere questi membri campi pubblici, come sotto?

 public class Point { public int x; public int y; } 

Per essere chiari, comprendo il valore di getter e setter quando è necessario eseguire una traduzione dei dati sottostanti. Ma nei casi in cui stai passando solo i valori, sembra inutilmente prolisso.

Tendo ad essere d’accordo (che sembra inutilmente prolisso), anche se questo è stato un problema che il nostro team non ha ancora risolto e quindi i nostri standard di codifica insistono ancora sulle proprietà prolisse per tutte le classi.

Jeff Atwood si è occupato di questo alcuni anni fa. Il punto più importante che ha notato a posteriori è che il passaggio da un campo a una proprietà è un cambiamento irrazionale nel codice; tutto ciò che lo consuma deve essere ricompilato per funzionare con la nuova interfaccia di class, quindi se qualcosa fuori dal tuo controllo sta consumando la tua class potresti avere problemi.

È anche molto più semplice cambiarlo in seguito:

 public int x { get; private set; } 

Incapsula l’impostazione e l’accesso di quei membri. Se da un po ‘di tempo uno sviluppatore del codice ha bisogno di modificare la logica quando si accede a un membro o impostarlo può essere fatto senza modificare il contratto della class.

L’idea è che anche se la struttura dati sottostante deve cambiare, l’interfaccia pubblica per la class non dovrà essere modificata.

C # può trattare le proprietà e le variabili in modo diverso a volte. Ad esempio, non è ansible passare proprietà come parametri di rif o out . Quindi se hai bisogno di cambiare la struttura dei dati per qualche motivo e stavi usando variabili pubbliche e ora hai bisogno di usare le proprietà, la tua interfaccia dovrà cambiare e ora il codice che accede alla proprietà x potrebbe non essere più compilato come quando era variabile X:

 Point pt = new Point(); if(Int32.TryParse(userInput, out pt.x)) { Console.WriteLine("x = {0}", pt.x); Console.WriteLine("x must be a public variable! Otherwise, this won't compile."); } 

L’uso delle proprietà sin dall’inizio lo evita e puoi tranquillamente modificare l’implementazione sottostante quanto necessario senza rompere il codice client.

Setter e Getter ti permettono di aggiungere ulteriore livello di astrazione e in puro OOP devi sempre accedere agli oggetti tramite l’interfaccia che stanno fornendo al mondo esterno …

Considera questo codice, che ti salverà in asp.net e che non sarebbe ansible senza il livello di astrazione fornito dai setter e dai getter:

 class SomeControl { private string _SomeProperty ; public string SomeProperty { if ( _SomeProperty == null ) return (string)Session [ "SomeProperty" ] ; else return _SomeProperty ; } } 

Dal momento che i getter auto-implementati prendono lo stesso nome per la proprietà e le reali variabili di archiviazione privata. Come puoi cambiarlo in futuro? Penso che il punto che viene detto è che usi l’auto implementato invece del campo in modo che tu possa cambiarlo in futuro se nel caso devi aggiungere la logica a getter e setter.

Per esempio:

 public string x { get; set; } 

e per esempio usi già xa molte volte e non vuoi rompere il tuo codice.

Come si modifica il settaggio auto getter … ad esempio per setter si consente solo l’impostazione di un formato di numero telefonico valido … come si modifica il codice in modo che solo la class debba essere modificata?

La mia idea è aggiungere una nuova variabile privata e aggiungere lo stesso x getter e setter.

 private string _x; public string x { get {return x}; set { if (Datetime.TryParse(value)) { _x = value; } }; } 

È questo che intendi rendendolo flessibile?

Va considerato anche l’effetto del cambiamento nei membri pubblici quando si tratta di binding e serializzazione. Entrambi questi spesso si basano su proprietà pubbliche per recuperare e impostare valori.

Inoltre, puoi mettere breakpoint su getter e setter, ma non puoi sui campi.

AFAIK l’interfaccia CIL generata è diversa. Se si modifica un membro pubblico in una proprietà, si modifica la sua interfaccia pubblica e occorre ribuild ogni file che utilizza tale class. Questo non è necessario se si modifica solo l’implementazione dei getter e setter.

Forse solo rendendo pubblici i campi potresti portarti ad un altro modello di dominio anemico .

Cordiali saluti

Vale anche la pena notare che non è ansible rendere Auto Proprietà in sola lettura e non è ansible inizializzarle in linea. Entrambe queste sono cose che mi piacerebbe vedere in una futura versione di .NET, ma credo che non si possa fare né in .NET 4.0.

Le uniche volte in cui utilizzo un backing field con proprietà in questi giorni è quando la mia class implementa INotifyPropertyChanged e ho bisogno di triggersre l’evento OnPropertyChanged quando viene modificata una proprietà.

Anche in queste situazioni ho impostato i campi di supporto direttamente quando i valori sono passati da un costruttore (non c’è bisogno di provare a lanciare OnPropertyChangedEvent (che sarebbe comunque NULL in questo momento), in qualsiasi altro modo io uso la proprietà stessa.

Non sai mai se potresti non aver bisogno di una traduzione dei dati più tardi. Sei preparato per questo se nascondi i tuoi membri. Gli utenti della tua class non noteranno se aggiungi la traduzione poiché l’interfaccia rimane la stessa.

Il più grande difrence è che, se cambi la struttura interna, puoi comunque mantenere getter e setter così come sono, cambiando la loro logica interna senza danneggiare gli utenti della tua API.

Se devi modificare la modalità con cui ottieni xey in questo caso, puoi aggiungere le proprietà in seguito. Questo è quello che trovo più confuso. Se si utilizzano variabili membro pubbliche, è ansible modificarle facilmente in una proprietà in un secondo momento e utilizzare variabili private denominate _x e _y se è necessario archiviare il valore internamente.

Setter e getter sono in linea di principio cattivi (sono un cattivo odore di OO – mi limiterò a dire che sono un anti-pattern perché a volte sono necessari).

No, tecnicamente non c’è alcuna differenza e quando voglio davvero condividere l’accesso a un object in questi giorni, di tanto in tanto lo faccio pubblico finale invece di aggiungere un getter.

Il modo in cui setter e getter sono stati “venduti” è che potrebbe essere necessario sapere che qualcuno sta ottenendo un valore o cambiandone uno – il che ha senso solo con i primitivi.

Oggetti di sacchetto di proprietà come DAO, DTO e oggetti di visualizzazione sono esclusi da questa regola perché non si tratta di oggetti in un vero significato “OO Design” della parola Oggetto. (Non pensi di “Passare messaggi” a un DAO, è semplicemente una pila di coppie di attributi / valori).