Incapsulamento significa che lo stato di un oggetto avviene solo attraverso un'interfaccia definita e per questo motivo la classe può assicurarsi che questo stato sia sempre valido e in linea con lo scopo della classe.
In alcuni casi, quindi, è perfettamente in linea con il principio di incapsulamento esporre pubblicamente un campo: tutti i valori possibili per il campo sono validi con tutti gli altri valori possibili di tutti gli altri campi, e quindi il programmatore può decidere attivamente di consentire il campo essere manipolato liberamente da codice esterno.
Questi casi sono però per lo più limitati a classi che sono per lo più "semplici vecchi dati". Inoltre non sono molto interessanti a questo proposito, quindi basta parlare di loro.
In altri casi, in altre lingue, si avrebbe un metodo getter e setter, qualcosa come int getId()ottenere un valore e void setId(int val)aggiornarlo.
Le proprietà ci consentono di utilizzare la stessa sintassi per la lettura e la scrittura attraverso i metodi che useremmo per leggere e scrivere un campo. Questo è un buon zucchero sintattico, anche se non vitale.
(In realtà, a causa del modo in cui funziona la riflessione e casi come DataBinder.Evalpuò essere utile avere una proprietà anche quando un campo funzionerebbe bene, ma questa è un'altra questione).
Fino all'introduzione dei setter privati (in realtà, ciò che è cambiato con C # 2 è la sintassi per avere un setter privato e un getter pubblico o protetto nello stesso blocco), potremmo avere un metodo privato per fare il lavoro del setter privato, quindi i setter privati non sono realmente necessari. Sono utili però, quindi mentre sono solo zucchero sintattico, sono piuttosto utili.
L'incapsulamento non è una questione se i tuoi setter (o getter) siano pubblici, privati, protetti o interni, ma una questione se sono appropriati . Inizia con un valore predefinito in cui ogni campo è privato (e del restoreadonly ) e quindi, se necessario, aggiungi membri (proprietà o metodi) che alterano quei campi e assicurati che l'oggetto rimanga valido mentre cambiano . Ciò garantisce che l' invariante di una classe sia mantenuta, il che significa che le regole che descrivono l'insieme di stati valido in cui può trovarsi non vengono mai violate (i costruttori aiutano anche assicurandosi che inizi in tale stato valido).
Per quanto riguarda la tua ultima domanda, essere immutabile significa che una classe non ha setter pubblici, protetti o interni e nessun metodo pubblico, protetto o interno che modifichi alcun campo. Ci sono gradi di questo, in C # ci sono tre gradi possibili:
Tutti i campi di istanza di una classe lo sono readonly, quindi anche il codice privato non può modificarlo. È garantito che sia immutabile (tutto ciò che tenta di cambiarlo non verrà compilato) e possibilmente le ottimizzazioni possono essere fatte sul retro di questo.
Una classe è immutabile dall'esterno perché nessun membro pubblico cambia nulla, ma non è garantito dall'uso di readonlynon essere modificata dall'interno.
Una classe è immutabile come vista dall'esterno, sebbene alcuni stati siano modificati come dettagli di implementazione. Ad esempio, un campo potrebbe essere memorizzato, e quindi mentre dall'esterno un tentativo di ottenerlo recupera solo lo stesso valore, il primo tentativo del genere lo calcola effettivamente e quindi lo memorizza per il recupero nei tentativi successivi.
private File settingsFile = null;e quindi in uno dei costruttori:if (settingsFile == null) { settingsFile = GetSettingsFile() };. Il refactoring del codice in questo modo mi ha fatto piangere a volte :). Solo perché puoi impostare un membro prima del costruttore, non significa che dovresti, poiché, con più costruttori, questo rende DIFFICILE seguire la logica. I setter privati ti obbligano a impostare i valori all'interno del costruttore o successivi.