Le proprietà di sola scrittura hanno applicazioni pratiche?

Non so perché ho iniziato a pensarci, ma ora non riesco a fermarmi.

In C # – e probabilmente in molte altre lingue, ricordo che Delphi era solito permettervi di fare anche questo: è legale scrivere questa syntax:

class WeirdClass { private void Hello(string name) { Console.WriteLine("Hello, {0}!", name); } public string Name { set { Hello(name); } } } 

In altre parole, la proprietà ha un setter ma non getter , è solo di scrittura .

Immagino che non riesca a pensare ad alcun motivo per cui questo dovrebbe essere illegale , ma non l’ho mai visto in natura, e mi sembra di essere un codice piuttosto geniale / orribile in natura. Sembra un odore di codice; sembra che il compilatore dovrebbe darmi un avvertimento:

CS83417: La proprietà ‘Nome’ sembra essere completamente inutile e stupida. Programmatore male! Considera la possibilità di sostituire con un metodo.

Ma forse non l’ho fatto abbastanza a lungo, o ho lavorato in un campo troppo ristretto per vedere qualche esempio dell’uso efficace di un tale costrutto.

Esistono esempi reali di proprietà di sola scrittura che non possono essere sostituiti da semplici chiamate di metodo o che diventerebbero meno intuitivi?

Le proprietà di sola scrittura sono in realtà abbastanza utili e le uso spesso. Si tratta di incapsulamento , che limita l’accesso ai componenti di un object. Spesso è necessario fornire uno o più componenti a una class che deve essere utilizzata internamente, ma non c’è motivo di renderli accessibili ad altre classi. Così facendo, la tua class diventa più confusa (“utilizzo questo metodo o questo metodo?”), E più probabilmente la tua class può essere manomessa o il suo vero scopo è aggirato.

Vedi “Perché i metodi getter e setter sono cattivi” per un’interessante discussione su questo. Non sono così serio come lo scrittore dell’articolo, ma penso che sia una buona cosa a cui pensare. Generalmente uso setter ma uso raramente i getter.

La mia prima reazione a questa domanda è stata: “Che dire del metodo java.util.Random # setSeed ?”

Penso che le sole proprietà di scrittura siano utili in diversi scenari. Ad esempio quando non si desidera esporre la rappresentazione interna ( incapsulamento ), mentre si consente di modificare lo stato dell’object. java.util.Random è un ottimo esempio di tale design.

Code Analysis (aka FxCop) ti dà una diagnosi:

CA1044 : Microsoft.Design: poiché la proprietà ‘WeirdClass.Name’ è di sola scrittura, aggiungere un getter di proprietà con un’accessibilità maggiore o uguale al suo setter o convertire questa proprietà in un metodo.

Ho un codice simile al seguente in un progetto XNA. Come puoi vedere, Scale è di sola scrittura, è utile e (ragionevolmente) intuitivo e una proprietà di lettura ( get ) non ha senso per questo. Certo, potrebbe essere sostituito con un metodo, ma mi piace la syntax.

 public class MyGraphicalObject { public double ScaleX { get; set; } public double ScaleY { get; set; } public double ScaleZ { get; set; } public double Scale { set { ScaleX = ScaleY = ScaleZ = value; } } // more... } 

Creare qualcosa di solo per scrivere è utile quando non devi leggere ciò che scrivi.

Ad esempio, quando si disegnano oggetti sullo schermo (questo è esattamente ciò che fa Desktop Window Manager in Windows):
Si può sicuramente disegnare su uno schermo, ma non si dovrebbe mai aver bisogno di leggere i dati (per non parlare di ottenere lo stesso disegno di prima).

Ora, se le proprietà di sola scrittura sono utili (al contrario dei metodi), non sono sicuro di quanto spesso vengano utilizzate. Suppongo che tu possa immaginare una situazione con una proprietà “BackgroundColor”, in cui scrivere su di essa imposta il colore di sfondo dello schermo, ma la lettura non ha senso (necessariamente).
Quindi non sono sicuro di questa parte, ma in generale volevo solo sottolineare che esistono casi d’uso per situazioni in cui si scrivono solo dati e non li si legge mai.

Un uso per una proprietà di sola scrittura è supportare l’iniezione della dipendenza setter, che viene in genere utilizzata per i parametri facoltativi.

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….

Ciò diventa particolarmente utile se la proprietà è definita in una class base. Se si è scelto l’injection construction per questa proprietà, è necessario scrivere un sovraccarico del costruttore in qualsiasi class derivata.

 public abstract class DisplayService { public WhizbangProvider Provider { set; private get; } } public class WhizbangService : DisplayService { } 

Qui, l’alternativa con l’iniezione del costruttore è:

 public abstract class DisplayService { public WhizbangProvider Provider; protected DisplayService(WhizbangProvider provider) { Provider = provider ?? new DefaultProvider(); } } public class WhizbangService : DisplayService { public WhizbangService(WhizbangProvider provider) : base(provider) { } } 

Questo approccio è per me più incasinato, perché è necessario ad alcuni dei meccanismi interni della class, in particolare, che se si passa null al costruttore, si otterrà un valore predefinito ragionevole.

Sebbene le linee guida di progettazione .NET raccomandino l’utilizzo di un metodo (“SetMyWriteOnlyParameter”) anziché di una proprietà di sola scrittura, trovo le proprietà di sola scrittura utili quando si creano oggetti collegati da una rappresentazione serializzata (da un database).

La nostra applicazione rappresenta i sistemi di produzione di giacimenti petroliferi. Abbiamo il sistema nel suo insieme (l’object “Modello”) e vari oggetti Reservoir, Well, Node, Group etc.

Il modello viene creato e letto dal database per primo: gli altri oggetti devono sapere a quale modello appartengono. Tuttavia, il modello deve sapere quale object inferiore rappresenta il totale delle vendite. Ha senso che queste informazioni siano archiviate in una proprietà Model. Se non vogliamo dover fare due letture delle informazioni del modello, dobbiamo essere in grado di leggere il nome dell’object Sales prima della sua creazione. Quindi, successivamente, impostiamo la variabile “SalesObject” in modo che punti all’object reale (in modo che, ad esempio, qualsiasi modifica da parte dell’utente del nome di questo object non causi problemi)

Preferiamo usare una proprietà di sola scrittura – ‘SalesObjectName = “TopNode”‘ – piuttosto che un metodo – ‘SetSalesObjectName (“TopNode”) – perché ci sembra che quest’ultimo suggerisca che SalesObject esiste.

Questo è un punto secondario, ma abbastanza da farci desiderare di usare una proprietà di sola scrittura.

Nel modello MVP è comune scrivere una proprietà con un setter sulla vista (non c’è bisogno di un getter) – ogni volta che il relatore lo imposta, la proprietà utilizzerà tale valore per aggiornare alcuni elementi dell’interfaccia utente.

Vedi qui per una piccola dimostrazione:

 public partial class ShowMeTheTime : Page, ICurrentTimeView { protected void Page_Load(object sender, EventArgs e) { CurrentTimePresenter presenter = new CurrentTimePresenter(this); presenter.InitView(); } public DateTime CurrentTime { set { lblCurrentTime.Text = value.ToString(); } } } 

Il metodo InitView del relatore imposta semplicemente il valore della proprietà:

 public void InitView() { view.CurrentTime = DateTime.Now; } 

Per quanto mi riguarda, non lo fanno. Ogni volta che ho usato una proprietà di sola scrittura come un attacco rapido, in seguito sono venuto a pentirmene. Di solito finisco con un costruttore o una proprietà completa.

Certo che sto cercando di dimostrare un aspetto negativo, quindi forse c’è qualcosa che mi manca.

Non posso nemmeno smettere di pensarci. Ho un caso d’uso per una proprietà “di sola scrittura”. Non riesco a vedere un buon modo per uscirne.

Voglio build un attributo C # che derivi da AuthorizeAttribute per un’applicazione ASP.NET MVC. Ho un servizio (diciamo, IStore) che restituisce informazioni che aiutano a decidere se l’utente corrente debba essere autorizzato. Iniezione del costruttore non funzionerà, perché

 public AllowedAttribute: AuthorizeAttribute { public AllowedAttribute(IStore store) {...} private IStore Store { get; set; } ... } 

rende memorizza un parametro di attributo posizionale, ma IStore non è un tipo di parametro di attributo valido e il compilatore non costruirà il codice annotato con esso. Sono costretto a ripiegare su Property Setter Injection.

 public AllowedAttribute: AuthorizeAttribute { [Inject] public IStore Store { private get; set; } ... } 

Insieme a tutte le altre cose negative su Setter di proprietà invece di Iniezione costruttore, il servizio è una proprietà di sola scrittura. Abbastanza grave da dover esporre il setter ai clienti che non dovrebbero avere bisogno di conoscere i dettagli dell’implementazione. Non farebbe alcun favore a nessuno di permettere ai clienti di vedere anche il getter.

Penso che il vantaggio di Dipendenza Iniezione superi le linee guida contro le proprietà di sola scrittura per questo scenario, a meno che mi manchi qualcosa.

No, posso immaginare un caso in cui non possono essere sostituiti, anche se potrebbero esserci persone che li considerano più leggibili.

Caso ipotetico:

 CommunicationDevice.Response = "Hello, World" 

invece di

 CommunicationDevice.SendResponse("Hello, World") 

Il compito principale sarebbe quello di eseguire effetti collaterali o validazione IO.

È interessante notare che VB .NET ha persino ottenuto la propria parola chiave per questo strano tipo di proprietà;)

 Public WriteOnly Property Foo() As Integer Set(value As Integer) ' ... ' End Set End Property 

anche se molte proprietà “solo scrittura” dall’esterno hanno effettivamente un getter privato.

Recentemente ho lavorato a un’applicazione che gestiva le password. (Nota che non sto sostenendo che quanto segue sia una buona idea, sto solo descrivendo quello che ho fatto.)

Ho avuto una class, HashingPassword , che conteneva una password. Il costruttore ha preso una password come argomento e l’ha memorizzata in un attributo privato. Dato uno di questi oggetti, è ansible acquisire un hash salato per la password o controllare la password con un dato hash salato. Non c’era, ovviamente, modo per recuperare la password da un object HashingPassword .

Allora ho avuto qualche altro object, non ricordo cosa fosse; facciamo finta che fosse una banana protetta da password. La class Banana aveva una proprietà set-only chiamata Password , che creava una HashingPassword dal valore dato e la memorizzava in un attributo privato di Banana . Poiché l’attributo password di HashingPassword era privato, non c’era modo di scrivere un getter per questa proprietà.

Quindi, perché ho avuto una proprietà set-only chiamata Password invece di un metodo chiamato SetPassword ? Perché aveva un senso. L’effetto era, infatti, di impostare la password di Banana , e se volessi impostare la password di un object Banana , mi aspetterei di farlo impostando una proprietà, non chiamando un metodo.

L’utilizzo di un metodo chiamato SetPassword non avrebbe avuto grossi svantaggi. Ma non vedo alcun vantaggio significativo, neanche.

So che è qui da molto tempo, ma l’ho trovato e ho un caso d’uso valido (imho):

Quando invii i parametri a una chiamata webapi da ajax, puoi semplicemente provare a compilare le proprietà della class dei parametri e includere la convalida o qualsiasi altra cosa.

 public int MyFancyWepapiMethod([FromBody]CallParams p) { return p.MyIntPropertyForAjax.HasValue ? p.MyIntPropertyForAjax.Value : 42; } public class CallParams { public int? MyIntPropertyForAjax; public object TryMyIntPropertyForAjax { set { try { MyIntPropertyForAjax = Convert.ToInt32(value); } catch { MyIntPropertyForAjax = null; } } } } 

Sul lato JavaScript puoi semplicemente compilare i parametri inclusa la convalida:

 var callparameter = { TryMyIntPropertyForAjax = 23 } 

che è sicuro in questo esempio, ma se gestisci userinput potrebbe non essere sicuro di avere un valore valido o qualcosa di simile.

Bene, tecnicamente non c’è nulla di sbagliato in una proprietà Setter-solo per sé. Il setter potrebbe fare qualche convalida, e forse altri metodi, pubblici o privati, potrebbero dipendere da esso.

Per esempio:

 class SomeClass { private int _someVar; SomeClass(int someVar) { if(someVar > 0) _someVar = someVar; } public int SomeVar { set { if(value > 0) _someVar = value; } } public string SomeFunction(){ return "This is an important function that uses _someVar " + _someVar; } } 

Quindi la dichiarazione stessa potrebbe essere legale e valida. Il tuo metodo body è un enorme odore di codice, ma questo non è il compito dei compilatori di cui preoccuparsi.