Mi piacerebbe approfondire un punto specifico che Eric Lippert ha fatto nella sua risposta e mettere i riflettori in un'occasione particolare che non è stata affatto toccata da nessun altro. Eric ha detto:
[...] un incarico lascia quasi sempre il valore che è stato appena assegnato in un registro.
Vorrei dire che il compito lascerà sempre indietro il valore che abbiamo cercato di assegnare al nostro operando di sinistra. Non solo "quasi sempre". Ma non lo so perché non ho trovato questo problema commentato nella documentazione. In teoria potrebbe essere una procedura implementata molto efficace per "lasciarsi alle spalle" e non rivalutare l'operando di sinistra, ma è efficace?
Sì "efficiente" per tutti gli esempi finora costruiti nelle risposte di questo thread. Ma efficiente nel caso di proprietà e indicizzatori che utilizzano accessor get e set? Affatto. Considera questo codice:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Qui abbiamo una proprietà, che non è nemmeno un wrapper per una variabile privata. Ogni volta che viene chiamato, tornerà vero, ogni volta che si tenta di impostare il proprio valore, non farà nulla. Pertanto, ogni volta che questa proprietà viene valutata, deve essere sincero. Vediamo cosa succede:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Indovina cosa stampa? Esso stampa Unexpected!!
. A quanto pare, viene effettivamente chiamato l'accessor set, che non fa nulla. Ma da allora in poi, l'accessor get non viene mai chiamato. L'assegnazione lascia semplicemente il false
valore che abbiamo cercato di assegnare alla nostra proprietà. E questo false
valore è ciò che valuta l'istruzione if.
Concluderò con un esempio del mondo reale che mi ha portato a ricercare questo problema. Ho creato un indicizzatore che era un comodo wrapper per una raccolta ( List<string>
) che una mia classe aveva come variabile privata.
Il parametro inviato all'indicizzatore era una stringa, che doveva essere trattata come un valore nella mia raccolta. L'accessorio get restituirebbe semplicemente vero o falso se quel valore esistesse o meno nell'elenco. Quindi l'accessor get era un altro modo di usare il List<T>.Contains
metodo.
Se l'accessor set dell'indicizzatore fosse chiamato con una stringa come argomento e l'operando giusto fosse un valore booleano true
, aggiungerebbe quel parametro all'elenco. Ma se lo stesso parametro fosse stato inviato all'accessor e l'operando giusto fosse un valore booleano false
, eliminerebbe invece l'elemento dall'elenco. Quindi l'accessor set è stato usato come una comoda alternativa a entrambi List<T>.Add
e List<T>.Remove
.
Pensavo di avere una "API" pulita e compatta che avvolgeva l'elenco con la mia logica implementata come gateway. Con l'aiuto di un solo indicizzatore ho potuto fare molte cose con alcune serie di tasti. Ad esempio, come posso provare ad aggiungere un valore al mio elenco e verificare che sia presente? Ho pensato che fosse l'unica riga di codice necessaria:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Ma come ha mostrato il mio esempio precedente, l'accessor get che dovrebbe vedere se il valore è davvero nell'elenco non è stato nemmeno chiamato. Il true
valore è stato sempre lasciato indietro, distruggendo efficacemente qualsiasi logica che avevo implementato nel mio get accessor.