2010-05-25 20 views
19

Eventuali duplicati:
Convention question: When do you use a Getter/Setter function rather than using a PropertyCosa getter e setter dovrebbe e non dovrebbe fare

ho incontrato un sacco di opinioni divergenti su getter e setter ultimamente, così ho pensato Dovrei farlo nella sua stessa domanda.

Un mio previous question ha ricevuto un commento immediato (successivamente cancellato) che i setter dichiarati non dovrebbero avere effetti collaterali e un metodo SetProperty sarebbe una scelta migliore.

In effetti, questo sembra essere Microsoft's opinion pure. Tuttavia, le loro proprietà spesso generano eventi, ad esempio Resized quando viene impostata la proprietà Width o Height di un modulo. OwenP afferma inoltre "non si deve consentire a una proprietà di generare eccezioni, le proprietà non dovrebbero avere effetti collaterali, l'ordine non dovrebbe avere importanza e le proprietà dovrebbero tornare relativamente rapidamente."

Eppure Michael Stum afferma che devono essere generate eccezioni durante la convalida dei dati all'interno di un setter. Se il setter non lancia un'eccezione, come è possibile validare efficacemente i dati, come suggeriscono molte delle risposte a this question?

Che dire quando è necessario generare un evento, come fanno quasi tutti i Control di Microsoft? Non sei quindi in balia di chi ha sottoscritto il tuo evento? Se il loro gestore esegue una quantità enorme di informazioni o genera un errore, cosa succede al tuo setter?

Infine, che dire di lazy loading all'interno del getter? Anche questo potrebbe violare le linee guida precedenti.

Che cosa è accettabile inserire in un getter o setter e cosa si deve tenere in solo i metodi di accesso?

Edit:

Da un altro article in MSDN:

I get e set metodi sono generalmente non è diverso da altri metodi. Possono eseguire qualsiasi logica di programma, generare eccezioni, essere sovrascritti e dichiarati con qualsiasi modificatore consentito dal linguaggio di programmazione. Notare, tuttavia, che le proprietà possono anche essere statiche. Se una proprietà è statica, esistono limitazioni su ciò che possono fare i metodi get e set. Vedere il riferimento del linguaggio di programmazione per i dettagli.

+0

Ho letto quella domanda, le risposte e anche collegata ad essa nella mia stessa domanda. Non ho visto nulla riguardo all'innalzamento degli eventi, che credo meriti risposte diverse. – dlras2

+1

@Rowland: Penso che sia abbastanza specifico per essere diverso. –

+2

Chi ha votato per la migrazione a Super User? – ChrisF

risposta

15

mio punto di vista:

  1. Se un setter o getter è prevista per essere costoso, non rendono una proprietà, ne fanno un metodo.

  2. Se l'impostazione di una proprietà attiva eventi a causa di modifiche, questo va bene. In quale altro modo si consente agli ascoltatori di essere informati delle modifiche? Tuttavia, potresti voler offrire una coppia BeginInit/EndInit per sopprimere gli eventi fino a quando non vengono apportate tutte le modifiche. Normalmente, è responsabilità del gestore eventi restituire tempestivamente, ma se davvero non ci si può fidare di farlo, allora si potrebbe desiderare di segnalare l'evento in un altro thread.

  3. Se l'impostazione di una proprietà genera eccezioni su valori non validi, va anche bene. Questo è un modo ragionevole per segnalare il problema quando il valore è completamente sbagliato. In altri casi, si imposta un gruppo di proprietà e quindi si chiama un metodo che le utilizza per fare qualcosa, ad esempio creare una connessione. Ciò consentirebbe di sospendere la convalida e la gestione degli errori fino a quando non verranno utilizzate le proprietà, quindi le proprietà non avrebbero bisogno di lanciare nulla.

  4. L'accesso a una proprietà può avere effetti collaterali a condizione che non siano inattesi e non contengano. Ciò significa che una istanza JIT in un getter va bene. Allo stesso modo, impostare un flag dirty per l'istanza ogni volta che viene apportata una modifica va bene, poiché imposta una proprietà correlata, ad esempio un formato diverso per lo stesso valore.

  5. Se lo fa qualcosa anziché accedere solo a un valore, dovrebbe essere un metodo. I metodi sono verbi, quindi la creazione di una connessione verrà eseguita dal metodo OpenConnection(), non da una proprietà Connection. Una proprietà di connessione verrebbe utilizzata per recuperare la connessione in uso o per associare l'istanza a un'altra connessione.

modifica - ha aggiunto 5, cambiato 2 e 3

1

Sono d'accordo con l'idea che getter/impostazioni non dovrebbero avere effetti collaterali, ma direi che non dovrebbero avere non- ovvi effetti collaterali.

Per quanto riguarda le eccezioni di lancio, se si imposta una proprietà su un valore non valido (in senso molto fondamentale), le eccezioni di convalida vanno bene. Tuttavia, se il setter sta eseguendo tutta una serie di complicate convalide delle regole di business, o sta tentando di uscire e aggiornare altri oggetti, o qualsiasi altra cosa che possa causare un'eccezione, allora è male. Ma questo problema non è davvero un problema con l'eccezione stessa, ma piuttosto che il setter sta andando fuori e sta eseguendo segretamente un sacco di funzionalità che il chiamante non dovrebbe (o non dovrebbe) aspettarsi.

Lo stesso con gli eventi. Se un setter sta lanciando un evento dicendo che "questa proprietà è cambiata", allora va bene, perché questo è un evidente effetto collaterale. Ma se sta sparando qualche altro evento personalizzato, quindi fa in modo che un pezzo di codice nascosto venga eseguito in un'altra parte di un sistema, è brutto.

Questa è la stessa ragione per cui evito il caricamento lento nei getter. In effetti, possono rendere le cose più semplici un sacco di tempo, ma possono rendere le cose un po 'più confuse alcune volte, perché finiscono sempre per essere contorte logiche intorno esattamente quando si vogliono caricare gli oggetti figli. Di solito è solo una riga di codice in più per caricare esplicitamente gli oggetti figlio quando si popola l'oggetto padre e si può evitare un sacco di confusione sullo stato dell'oggetto. Ma questo aspetto può diventare molto soggettivo e molto dipende dalla situazione.

+0

Evitare la pigrizia può avere notevoli costi di performance, quindi non penso che sia una buona idea in generale. –

+1

@Steven Sundit: Sì, è un compromesso e dipende dalla situazione. Se il pre-caricamento introduce costi di prestazione secondari, quindi perseguire il caricamento lento o il caricamento più specifico per la situazione. Ma spesso (e nella mia esperienza, di solito) non è così. –

+0

Il pre-caricamento può certamente essere una scelta appropriata, in particolare quando costa poco o è una tantum. Quando non è né l'uno né l'altro, allora JIT diventa una mossa forzata. –

0

Ho sempre trovato l'approccio conservativo per essere il migliore, quando si lavora in C# in ogni caso. Poiché le proprietà sono sintatticamente uguali ai campi, dovrebbero funzionare come i campi: nessuna eccezione, nessuna convalida, nessun business divertente. (In effetti, la maggior parte delle mie proprietà iniziano come campi semplici e non diventano proprietà fino a quando non sono assolutamente necessarie.) L'idea è che se vedi qualcosa che assomiglia o ottiene un set di campi, allora è qualcosa come ottenere o impostazione di un campo, in termini di funzionalità (non viene generata alcuna eccezione), l'efficienza generale (le variabili di impostazione non innescano una cascata di chiamate delegate, ad esempio) e l'effetto sullo stato del programma (l'impostazione di una variabile imposta tale variabile e non lo fa) t chiamare molti delegati che potrebbero fare qualsiasi cosa).

cose sensibili per un insieme di proprietà per fare includono l'impostazione di un flag per indicare che c'è stato un cambiamento:

set { 
    if(this.value!=value) { 
     this.changed=true; 
     this.value=value; 
    } 
} 

Forse in realtà impostare un valore su un altro oggetto, ad esempio:

set { this.otherObject.value=value; } 

Forse districare un po 'l'input, per semplificare il codice interno della classe:

set { 
    this.isValid=(value&Flags.IsValid)!=0; 
    this.setting=value&Flags.SettingMask; 
} 

(O Naturalmente, in questi ultimi due casi, la funzione get potrebbe fare esattamente l'opposto.)

Se qualcosa di più complicato deve accadere, in particolare chiamare delegati, o eseguire la convalida, o lanciare eccezioni, allora il mio punto di vista è che un la funzione è migliore (Molto spesso, i miei campi diventano proprietà con get e set, e quindi finiscono come proprietà get e funzione set.) Analogamente per i getter; se stai restituendo un riferimento a qualcosa, non è un problema, ma se stai creando un oggetto grande completamente nuovo e lo riempi ogni volta che la proprietà viene letta, non così caldo.

+0

Se lavori in C#, devi essere a conoscenza di WPF, che dipende interamente dalle modifiche alle proprietà che hanno l'effetto collaterale di segnalare tali cambiamenti. Non vedo come possiamo seguire il tuo consiglio senza paralizzare il nostro codice. –

+0

Quando si pronuncia "l'effetto collaterale della segnalazione di tali modifiche", si riferisce all'innalzamento di un evento? O stai dicendo che 'if (this.value! = Value) return;' sarebbe una cattiva idea? Non ho lavorato con WPF. (Ad esempio, programma in C#.) – dlras2

+0

@Cyclotis: questo vale per più di un semplice WPF, ma WPF fa un uso particolarmente evidente della notifica di modifica delle proprietà. Troverai anche esempi in molti luoghi in cui viene utilizzato il collegamento dati. Se non siamo autorizzati ad attivare eventi quando c'è un cambiamento, cosa abbiamo invece? Polling? –