Proprietà di sola scrittura, qual è il punto?

Capisco perché vorresti usare una proprietà di sola lettura usando la seguente syntax:

private int _MyInt; public int MyInt { get { return _MyInt; } } 

Questo esempio probabilmente non è il migliore perché penso che le proprietà di readonly brillino davvero in congiunzione con una variabile di readonly , ma questo è oltre il punto. Quello che non capisco è perché usare una proprietà di sola scrittura usando la seguente syntax:

 private int _MyInt; public int MyInt { set { _MyInt = value; } } 

Questo è il modo in cui le proprietà di sola lettura sono descritte in vari libri ed esercitazioni. Se imposti la variabile, la leggeresti concettualmente ad un certo punto, almeno internamente alla class, ma per leggerla anche internamente all’interno della class lo faresti _MyInt che mi sembra violi lo spirito dell’incapsulamento che le proprietà cercano di imporre. Invece, perché non dovresti semplicemente usare tutta la potenza della proprietà con modifiche di accesso diverse per accedervi come tale:

 private int _MyInt; public int MyInt { set { _MyInt = value; } private get { return _MyInt; } } 

Che, naturalmente, può essere solo scritto

 public int MyInt { set; private get; } 

Si ottiene comunque l’incapsulamento, ma si restringono le altre classi dall’accesso, quindi è ancora in scrittura solo per le classi esterne.

A meno che non ci sia un caso in cui onestamente vorremmo assegnare a una variabile, ma mai effettivamente accedervi, nel qual caso sarei sicuramente curioso di sapere quando questa necessità si presenterebbe.

Non ho mai incontrato un caso d’uso valido per una proprietà di sola scrittura. Onestamente, se esiste un caso d’uso valido per una proprietà di sola scrittura, penso che sia sicuro dire che la soluzione è mal progettata.

Se hai bisogno di semantica “sola scrittura” dovresti usare un metodo. Ad esempio, un altro utente ha trovato un esempio di un object utente che utilizza una proprietà di sola scrittura per impostare una password. Questo è un cattivo design:

 class User { public string Password { set { /* password encryption here */ } } } 

Ugh. Questo è molto meglio:

 class User { public void SetPassword(string password) { /* password encryption here */ } } 

Vedi, una proprietà di lettura / scrittura è un insieme di metodi che sono progettati per mascherarsi come un campo. Sembrano e si sentono come un campo. È per questo motivo che una proprietà di sola lettura ha senso perché siamo abituati ad avere campi e variabili che possiamo leggere ma non possiamo cambiare. Tuttavia non esiste un campo corrispondente o un costrutto variabile che sia scrivibile ma non leggibile.

Questo è il motivo per cui credo che la creazione di un’API che impiega proprietà di sola scrittura sia una ctriggers pratica. Funziona contro-intuitivo a quello che credo sia l’objective principale della syntax della proprietà in C #.

Modifica: Più filosofia … Credo che le classi abbiano uno scopo funzionale: forniscono un contenitore per i dati correlati da conservare e manipolare. Prendiamo ad esempio la nostra class User – questa class contiene tutte le informazioni che riguardano un utente nel sistema. Raccogliamo tutti questi dati e diamo loro un unico nome: utente . In questo modo usiamo le classi per creare astrazioni . User è un’astrazione che ci consente di ragionare su tutti i singoli dati che compongono un utente (password, nome, compleanno, ecc.).

Ora ci sono buone astrazioni e ci sono cattive astrazioni. Credo che le proprietà di sola scrittura siano brutte astrazioni perché si consente a qualcuno di inserire dati e non leggerli. Perché non vorresti questo? Molto probabilmente perché le informazioni che sono state trasmesse sono state trasformate in un modo che lo rende illeggibile per il passante.

Questo significa che una proprietà di sola scrittura, per definizione, deve creare effetti collaterali che il chiamante non può vedere (perché se fossero in grado di vederli, non ci sarebbe alcun motivo per rendere la proprietà di sola scrittura). Il miglior costrutto nel linguaggio C # per l’impostazione di un valore con effetti collaterali è il metodo .

Consiglio vivamente di non usare proprietà di sola scrittura perché i consumatori della tua API li troveranno confusi e frustranti. Anche se trovi un caso d’uso valido per questa syntax, non ne giustifica l’uso.


Modifica: Ecco la raccomandazione ufficiale da .Net Linee guida per la progettazione di framework per lo sviluppo di librerie di classi -> Linee guida per la progettazione dei membri -> Progettazione di proprietà

Non fornire proprietà di solo set.

Se il getter della proprietà non può essere fornito, utilizzare un metodo per implementare la funzionalità. Il nome del metodo dovrebbe iniziare con Set seguito da quale sarebbe stato il nome della proprietà …

Domanda interessante

Dopo un po ‘su Google, questo è tutto ciò che ho potuto trovare: una proprietà di sola scrittura può essere utilizzata per impostare una password su un object User . Poiché le password vengono solitamente archiviate in formato hash, non c’è (e dovrebbe essere) alcun modo per recuperare una password dopo che è stata impostata.

Un uso per una proprietà di sola scrittura è supportare l’iniezione delle dipendenze setter.

Diciamo che ho avuto una lezione:

 public class WhizbangService { public WhizbangProvider Provider { set; private get; } } 

Il WhizbangProvider non è destinato a essere accessibile dal mondo esterno. Non vorrei mai interagire con il service.Provider , è troppo complesso. Ho bisogno di una class come WhizbangService per fungere da facciata. Eppure con il setter, posso fare qualcosa del genere:

 service.Provider = new FireworksShow(); service.Start(); 

E il servizio inizia uno spettacolo pirotecnico. O forse preferiresti vedere uno spettacolo di acqua e luci:

 service.Stop(); service.Provider = new FountainDisplay(new StringOfLights(), 20, UnitOfTime.Seconds); service.Start(); 

E così via….

Un caso comune che posso vedere è se si desidera che un chiamante sia in grado di ottenere una versione trasformata della proprietà. Per esempio:

 public int MyInt { set; } public string MyIntAsString { get { return this.MyInt.ToString(); } } 

Ovviamente, questo non è un esempio reale, ma ottieni l’immagine.

MODIFICA :

Dopo aver letto il caso d’uso di Thomas, un vero scenario di tipo “trasformazione” potrebbe essere quello di recuperare una versione crittografata della proprietà di sola scrittura:

 private string password; public string ClearPassword { set { this.password = value; } public string EncryptedPassword { get { return Encrypt(password); } } 

Le fonti che ho letto hanno suggerito che se hai onestamente una situazione in cui hai appena creato una proprietà di sola scrittura, dovresti probabilmente considerare di trasformarla in un metodo. E di solito sono d’accordo.

Qualsiasi informazione che deve fluire in un modo è utile attraverso una proprietà di sola scrittura. Generalmente scriviamo classi per le informazioni che fluiscono fuori o dentro e fuori. Nel caso della sola scrittura, devi pensare a situazioni in cui le informazioni dovrebbero solo fluire e non uscire. Di solito questo comporta un tipo di informazione di sicurezza perché non vogliamo che un utente abbia accesso al recupero di informazioni sicure. Password, numeri CC, ecc …

Quando le informazioni non sono sicure, per esempio, quando non ci importa che qualcuno vi acceda, otteniamo il comune schema get / set. Il 99,9% delle volte questo è il modo in cui vengono utilizzate le proprietà. Dato che ci sono altri modi, altrettanto semplici che possono essere più robusti per ottenere questo, questo è più di un costrutto semantico che è stato lasciato alla simmetria più di ogni altra cosa. Uno dei rari casi in cui qualcosa non è stato eliminato e la tua sinistra con “Perché diamine hanno rimosso quello!”.

La ragione per cui riesco a pensare in cima alla mia testa è perché _myInt si presenta in cima al menu contestuale di intellisense.

Non credo che l’accesso a _myInt violi lo spirito dell’incapsulamento; è un campo membro privato di una class. Qualsiasi codice all’interno della class DOVREBBE usare il _myInt. È privato, dopo tutto.

L’unica cosa che importa al mondo esterno è il contratto pubblico della class. _myInt è una preoccupazione della class stessa, non parte del contratto pubblico.

E come ho detto – è più facile afferrare _myInt con intellisense quando stai codificando.

Sono sicuro che la funzione è semplicemente per la simmetria con una variabile get-only. Come Aphex implica vagamente, alcune cose non hanno senso e sono lì solo perché è più facile lasciarle dentro che rimuoverle.

Ho avuto una causa per una proprietà di sola scrittura.

In un servizio Web .NET ho dovuto creare un servizio web .ASMX legacy, poiché l’URI è hardcoded nell’hardware.

Sono un grande fan dell’uso di Ninject per l’iniezione delle dipendenze, utilizzando l’iniezione del costruttore per soddisfare tutte le dipendenze di una class. Per la maggior parte, queste dipendenze sarebbero dichiarate private readonly fieldName e assegnate al costruttore. La ragione di ciò è che le dipendenze della class non fanno parte delle responsabilità della class, sono delle dipendenze.

Tuttavia, il problema è che qualsiasi meccanismo factory utilizzato da ASP.NET per creare l’istanza di class per un servizio Web si basa sul fatto che esiste un costruttore senza argomenti, che ha interrotto l’iniezione del costruttore.

Avrei potuto usare una chiamata dal costruttore predefinito per redirect a un costruttore sovraccarico usando un pattern ServiceLocator (anti) ma non mi piaceva.

Usando l’estensione web di Ninject, ho ereditato la mia class di servizio da Ninject.Web.WebServiceBase e Ninject.Web.WebServiceBase utilizzato l’iniezione di proprietà, contrassegnando le proprietà che Ninject avrebbe soddisfatto con un attributo [Inject] .

Tornando al punto in cui i servizi iniettati sono le dipendenze per la class, piuttosto che le responsabilità della class, non voglio che queste proprietà di dipendenza siano accessibili da altre classi, quindi contrassegno la proprietà get come privata.