Garantire l'immutabilità è una giustificazione per esporre un campo anziché una proprietà?


10

La guida generale per C # è utilizzare sempre una proprietà su un campo pubblico. Questo ha senso: esponendo un campo, stai esponendo molti dettagli di implementazione. Con una proprietà, incapsuli quel dettaglio in modo che sia nascosto dal consumo di codice e le modifiche all'implementazione siano disaccoppiate dalle modifiche dell'interfaccia.

Tuttavia, mi chiedo se a volte esiste una valida eccezione a questa regola quando si tratta della readonlyparola chiave. Applicando questa parola chiave a un campo pubblico, si fornisce una garanzia aggiuntiva: immutabilità. Questo non è solo un dettaglio di implementazione, l'immutabilità è qualcosa che potrebbe interessare un consumatore. L'uso di un readonlycampo lo rende parte del contratto pubblico e qualcosa che non può essere rotto da future modifiche o eredità senza dover anche modificare l'interfaccia pubblica. È qualcosa che una proprietà non può offrire.

Quindi garantire l'immutabilità è un motivo legittimo per scegliere un readonlycampo piuttosto che una proprietà in alcuni casi?

(Per chiarimenti, non sto certamente dicendo che dovresti sempre fare questa scelta solo perché il campo sembra essere immutabile al momento, solo quando ha senso come parte del design della classe e è previsto l'uso per includere l'immutabilità nel suo contratto. Sono per lo più interessato a risposte incentrate sul fatto che ciò possa essere giustificato, piuttosto che su casi specifici in cui non lo è, ad esempio quando è necessario che il membro si trovi su un interfaceo si desideri eseguire il caricamento lento.)



1
@gnat Solo per chiarire, con "Proprietà ReadOnly" stanno usando la terminologia VB.NET che significa una proprietà senza setter, non un campo di sola lettura. Ai fini della mia domanda, le due opzioni che confronta quella domanda sono equivalenti: utilizzano entrambe le proprietà.
Ben Aaronson,

Risposte:


6

I campi di sola lettura statici pubblici vanno certamente bene. Sono consigliate per determinate situazioni, ad esempio quando si desidera un oggetto con nome e costante ma non è possibile utilizzare la constparola chiave.

I campi dei membri di sola lettura sono un po 'più complicati. Non c'è molto vantaggio ottenuto su una proprietà senza un setter pubblico e c'è un grosso svantaggio: i campi non possono essere membri di interfacce. Quindi, se decidi di refattare il tuo codice in modo che operi su un'interfaccia anziché su un tipo concreto, dovrai modificare i campi di sola lettura in proprietà con getter pubblici.

Una proprietà pubblica non virtuale senza un setter protetto fornisce protezione contro l'ereditarietà, a meno che le classi ereditate non abbiano accesso al campo di supporto. È possibile applicare completamente quel contratto nella classe base.

Non essere in grado di cambiare l '"immutabilità" del membro senza cambiare l'interfaccia pubblica della classe non è molto un ostacolo in questo caso. Di solito, se si desidera modificare un campo pubblico in una proprietà, procede senza intoppi, tranne per il fatto che ci sono due casi da affrontare:

  1. Tutto ciò che scrive a un membro di quell'oggetto, come: object.Location.X = 4è possibile solo se Locationè un campo. Questo non è rilevante per un campo di sola lettura, poiché non è possibile modificare una struttura di sola lettura. (Si presume che Locationsia un tipo di valore - in caso contrario, questo problema non si applicherebbe comunque perché readonlynon protegge da quel genere di cose.)
  2. Qualsiasi chiamata a un metodo che passa il valore come outo ref. Ancora una volta, questo non è realmente rilevante per un campo di sola lettura, perché oute i refparametri tendono a essere modificati, ed è comunque un errore del compilatore passare un valore di sola lettura come out o ref.

In altre parole, sebbene la conversione di un campo di sola lettura in una proprietà di sola lettura rompa la compatibilità binaria, è necessario ricompilare altro codice sorgente senza modifiche per gestire questa modifica. Questo rende la garanzia davvero facile da rompere.

Non vedo alcun vantaggio nel readonlycampo membro e l'incapacità di avere quel genere di cose su un'interfaccia è abbastanza uno svantaggio per me che non lo userei.


Per interesse, perché i campi di sola lettura statici pubblici vanno bene? È solo perché escludendo i metodi di istanza si rimuovono la maggior parte dei casi d'uso per le proprietà su campi immutabili (come il conteggio degli hit, il caricamento lento, ecc.)?
Ben Aaronson,

Il principale svantaggio di un campo di sola lettura pubblico rispetto alla proprietà di sola lettura è scomparso: i membri statici non possono far parte di interfacce, quindi sono su basi simili. Una proprietà statica di sola lettura ha bisogno di memoria che viene inizializzata o deve ricostruire il suo valore di ritorno ogni volta che viene chiamata, mentre un campo di sola lettura avrà una sintassi più semplice e verrà inizializzato dal costruttore statico. Quindi penso che un campo statico statico di sola lettura abbia il potenziale per una maggiore ottimizzazione da parte della JIT senza nessuno degli svantaggi che normalmente avresti se esponessi un campo.
Erik,

2

Dalla mia esperienza, la readonlyparola chiave non è la magia che promette.

Ad esempio, hai una classe in cui il costruttore era semplice. Dato l'uso (e il fatto che alcune proprietà / campi della classe sono immutabili dopo la costruzione) potresti pensare che non abbia importanza, quindi hai usato la readonlyparola chiave.

Più tardi, la classe diventa più complessa, così come lo è il suo costruttore. (Supponiamo che ciò accada in un progetto che è sperimentale o che ha una velocità sufficientemente elevata per lo zoom fuori dall'ambito / controllo, ma è necessario farlo funzionare in qualche modo.) Scoprirai che i campi che readonlypossono essere modificati solo all'interno del costruttore - tu non può modificarlo nei metodi anche se quel metodo viene chiamato solo da un costruttore.

Questa limitazione tecnica è stata riconosciuta dai progettisti di C # e potrebbe essere risolta in una versione futura. Ma fino a quando non viene risolto, l'uso di readonlyè più restrittivo e potrebbe incoraggiare qualche cattivo stile di codifica (mettendo tutto nel metodo del costruttore).

Come promemoria, né la proprietà readonlyné il setter non pubblico fornisce alcuna garanzia di un "oggetto completamente inizializzato". In altre parole, è il progettista di una classe che decide cosa significa "completamente inizializzato" e come proteggerlo da un uso involontario, e tale protezione generalmente non è infallibile, nel senso che qualcuno potrebbe trovare un modo per aggirarlo.


1
Preferisco mantenere semplici i costruttori - vedi brendan.enrick.com/post/… , blog.ploeh.dk/2011/03/03/InjectionCostructorsdovrebbe essere abbastanza semplice , in effetti, incoraggia di sola lettura un buon stile di codifica
AlexFoxGill

1
Un costruttore può passare un readonlycampo come parametro outo refad altri metodi, che saranno quindi liberi di modificarlo come ritengono opportuno.
supercat,

1

I campi sono SEMPRE dettagli di implementazione. Non vuoi esporre i tuoi dettagli di implementazione.

Un principio fondamentale dell'ingegneria del software è che non sappiamo quali saranno i requisiti in futuro. Sebbene il tuo readonlycampo possa essere buono oggi, non c'è nulla che possa suggerire che farà parte dei requisiti di domani. Cosa succede se domani è necessario implementare un contatore di visite per determinare quante volte si accede a quel campo? Cosa succede se è necessario consentire alle sottoclassi di sostituire il campo?

Le proprietà sono così facili da usare e sono molto più flessibili dei campi pubblici che non riesco a pensare a un singolo caso d'uso in cui sceglierei c # come la mia lingua e utilizzerei i campi di sola lettura pubblici in una classe. Se le prestazioni fossero così ridotte da non riuscire a prendere il colpo della chiamata di metodo extra, probabilmente userei una lingua diversa.

Proprio perché siamo in grado di fare qualcosa con la lingua non significa che dovremmo fare qualcosa con la lingua.

In realtà mi chiedo se i designer linguistici si pentano di aver reso pubblici i campi. Non ricordo l'ultima volta in cui ho persino preso in considerazione l'uso di campi pubblici non costanti nel mio codice.


1
Capisco l'importanza di costruire nella futura flessibilità, ma sicuramente se scrivo una classe come, diciamo, Rationalcon pubblico, immutabile Numeratore Denominatormembri, potrei essere estremamente sicuro che quei membri non richiederanno il caricamento lento o un hit counter o simile?
Ben Aaronson,

Ma puoi essere sicuro che l'implementazione usi i floattipi per Numeratore Denominatornon sarà necessario cambiarla doubles?
Stephen,

1
Bene, ehm, no, sicuramente non dovrebbero essere né floatné né double. Dovrebbero essere int, longo BigInteger. Forse quelli hanno bisogno di cambiare ... ma in tal caso dovrebbero cambiare anche nell'interfaccia pubblica, quindi usare davvero le proprietà non aggiunge incapsulamento attorno a quel dettaglio.
Ben Aaronson,

-3

No. Non è una giustificazione.

Usare il readonly come garanzia di immutabilità in non una giustificazione è più simile a un hack.

In sola lettura significa che il valore viene assegnato da un costruttore o quando viene definita la variabile.

Penso che sarà una cattiva pratica

Se vuoi davvero garantire l'immutabilità usa un'interfaccia che ha più vantaggi che svantaggi.

Esempio di interfaccia semplice: inserisci qui la descrizione dell'immagine


1
"usa un'interfaccia" Come?
Ben Aaronson,
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.