Quando e perché utilizzare void (anziché ad esempio bool / int)


30

Occasionalmente mi imbatto in metodi in cui uno sviluppatore ha scelto di restituire qualcosa che non è critico per la funzione. Voglio dire, quando guardo il codice, apparentemente funziona altrettanto bene come voide dopo un momento di riflessione, chiedo "Perché?" Questo suona familiare?

A volte concordo sul fatto che molto spesso è meglio restituire qualcosa come un boolo int, piuttosto che semplicemente fare un void. Non sono sicuro però, in generale, dei pro e dei contro.

A seconda della situazione, la restituzione di an intpuò rendere il chiamante consapevole della quantità di righe o oggetti interessati dal metodo (ad esempio, 5 record salvati in MSSQL). Se un metodo come "InsertSomething" restituisce un valore booleano, posso avere il metodo progettato per restituire in truecaso di successo, altrimenti false. Il chiamante può scegliere di agire o meno su tali informazioni.

D'altro canto,

  • Può portare a uno scopo meno chiaro di una chiamata di metodo? Una codifica errata spesso mi costringe a ricontrollare il contenuto del metodo. Se restituisce qualcosa, ti dice che il metodo è di un tipo che si deve fare qualcosa con il risultato restituito.
  • Un altro problema potrebbe essere, se l'implementazione del metodo non ti è nota, cosa ha deciso di restituire lo sviluppatore che non è fondamentale per la funzione? Ovviamente puoi commentarlo.
  • Il valore restituito deve essere elaborato quando l'elaborazione potrebbe essere terminata nella parentesi di chiusura del metodo.
  • Cosa succede sotto il cofano? Il metodo chiamato è stato ricevuto a falsecausa di un errore generato? O è tornato falso a causa del risultato valutato?

Quali sono le tue esperienze con questo? Come agiresti su questo?


1
Una funzione che restituisce il vuoto non è tecnicamente una funzione. È solo un metodo / procedura. La restituzione voidalmeno consente allo sviluppatore di sapere che il valore restituito dal metodo è irrilevante; fa un'azione, piuttosto che calcolare un valore.
KChaloux,

Risposte:


32

Nel caso di un valore di ritorno bool per indicare il successo o il fallimento di un metodo, preferisco il Tryparadigma -prefix utilizzato in vari metodi .NET.

Ad esempio, un void InsertRow()metodo potrebbe generare un'eccezione se esiste già una riga con la stessa chiave. La domanda è: è ragionevole supporre che il chiamante assicuri che la sua riga sia unica prima di chiamare InsertRow? Se la risposta è no, fornirei anche un valore bool TryInsertRow()che restituisce false se la riga esiste già. In altri casi, come errori di connettività db, TryInsertRow potrebbe comunque generare un'eccezione, supponendo che il mantenimento della connettività db sia responsabilità del chiamante.


4
+1 per fare la distinzione tra gestione di guasti previsti e imprevisti - la mia risposta non ha sottolineato abbastanza questo.
Péter Török,

Assolutamente un +1 per cercare di inventare un principio per i metodi TryXX che apparentemente rendono sia il metodo try che il metodo principale più facili da mantenere e più facili da capire il loro scopo.
Indipendente

+1 Ben detto. La chiave per la migliore risposta a questa domanda è che a volte si desidera dare al chiamante l'opzione di un metodo assertivo che genererà un'eccezione. In questi casi, tuttavia, l'eccezione non dovrebbe essere colta e gestita in modo falso dal metodo assertivo. Il punto è che il chiamante è reso responsabile. D'altra parte, un paradigma Try (o Monade) dovrebbe catturare e gestire le eccezioni, quindi restituire un bool o un Enum descrittivo se sono necessari maggiori dettagli. Si noti che un chiamante si aspetta che un tentativo / monade non genererà MAI un'eccezione. È come un punto di ingresso.
Suamere,

Pertanto, poiché si prevede che non si verifichi mai un'eccezione da Try / Monad, tuttavia un bool non è abbastanza descrittivo per informare il chiamante che una connessione db non è riuscita o che una richiesta http ha uno dei molti valori di ritorno necessari per agire in modo diverso, puoi usare un Enum invece di un bool per il tuo Try / Monad.
Suamere,

Se si dispone di TryInsertRow - che restituisce un valore booleano - se, ad esempio, esiste più di un vincolo univoco nel database - come si fa a sapere esattamente quale errore - e comunicarlo all'utente? Dovrebbe essere usato un tipo di ritorno più ricco contenente il messaggio di errore / codice e bool di successo?
Niico,

28

L'IMHO che restituisce un "codice di stato" deriva da epoche storiche, prima che le eccezioni diventassero comuni in linguaggi di livello medio-alto come C #. Oggi è meglio generare un'eccezione se qualche errore imprevisto ha impedito il successo del metodo. In questo modo è sicuro che l'errore non passa inosservato e il chiamante può gestirlo come appropriato. Quindi, in questi casi, è perfettamente corretto che un metodo non restituisca nulla (ovvero void) invece di un flag di stato booleano o di un codice di stato intero. (Naturalmente si dovrebbe documentare quali eccezioni il metodo può generare e quando.)

D'altra parte, se si tratta di una funzione in senso stretto, ovvero esegue un calcolo basato su parametri di input e si prevede che restituisca il risultato di tale calcolo, il tipo restituito è ovvio.

C'è una zona grigia tra i due, quando si può decidere di restituire alcune informazioni "extra" dal metodo, se ritenuto utile per i suoi chiamanti. Come il tuo esempio sul numero di righe interessate in un inserto. Oppure, per un metodo per inserire un elemento in una raccolta associativa, può essere utile restituire il valore precedente associato a quella chiave, se presente. Tali usi possono essere identificati analizzando attentamente gli scenari di utilizzo (noti e previsti) di un'API.


4
+1 per le eccezioni al codice di stato. L'eccezione (cattivo gioco di parole previsto) è il modello TryXX ben utilizzato.
Andy Lowry,

io sono con te lì. TcF e non zittire errori! Probabilmente, tuttavia, la zona grigia. Ci può sempre essere utile restituire qualcosa. Righe interessate, ultimo ID inserito, un PID di software in esecuzione, ma penso ancora che sia più facile pensare "diavolo sì, lo restituisco" durante lo sviluppo. Quindi, un anno dopo, quando si estende, "che cosa? Questo non fa int alcuniThing = RunProcess (x) 'cosa succede se non può funzionare?"
Indipendente

+1 informazioni extra è il motivo per cui codifico metodi come questo in linguaggi di livello superiore come C #. Ciò semplifica l'utilizzo delle informazioni extra quando ne hai bisogno.
ashes999,

2
-1 Caspita, le tue parole sono così contraddittorie e sbagliate. Non utilizzare mai eccezioni per il flusso di controllo o la comunicazione. Sono infinitamente più costosi di un condizionale. Quando è il tempo "prima che le eccezioni diventassero banali"? ... intendi prima che i blocchi try / catch diventino banali? Le eccezioni sono comuni da sempre. "Lancia un'eccezione se si verifica un errore imprevisto ..." Come si agisce su qualcosa di inaspettato? Perché dovresti prendere un'eccezione, quindi rilanciarla? È così costoso. Perché dici "errore"? Errori ed eccezioni sono cose diverse.
Suamere,

1
E chi può dire che un'eccezione generata non passerà inosservata? Nulla costringe nessuno ad accedere a un blocco catch, perché non accedere a un blocco "then"? Perché "documentare quali eccezioni il metodo può generare e quando"? Che ne dici se scriviamo un codice autocompensante e un metodo restituisce semplicemente un enum con gli stati finali previsti di quel metodo? Se è possibile evitare una possibile eccezione controllando lo stato, devono essere catturati e gestiti senza lanciare.
Suamere,

14

La risposta di Peter copre bene le eccezioni. Altre considerazioni qui riguardano la separazione comandi-query

Il principio CQS afferma che un metodo dovrebbe essere un comando o una query e non entrambi. I comandi non devono mai restituire un valore, modificare solo lo stato e le query devono solo restituire e non modificare lo stato. Ciò mantiene la semantica molto chiara e aiuta a rendere il codice più leggibile e gestibile.

Ci sono alcuni casi che violano il principio CQS è una buona idea. Di solito si tratta di prestazioni o sicurezza dei thread.


1
-1 Sbagliato e Sbagliato. In primo luogo, il punto centrale di CQS sta nel Q. Un chiamante dovrebbe essere in grado di ottenere calcoli o chiamate esterne attraverso un servizio con stato senza timore di alterare lo stato di quel servizio. Inoltre, mentre CQS è un principio utile da seguire in modo approssimativo, è rigorosamente seguito solo in progetti specifici. Infine, anche in CQS rigorosi, nulla dice che un comando non può restituire un valore, dice che non dovrebbe essere il calcolo o la chiamata di calcoli esterni e la restituzione dei dati . O C o Q possono sentirsi liberi di restituire il loro stato finale, se ciò è utile per il chiamante.
Suamere,

Ah, ma il numero di righe interessate da un comando rende molto più semplice la creazione di nuovi comandi da quelli vecchi. Fonte: anni e anni di esperienza.
Joshua,

@Joshua: Allo stesso modo, "aggiungi nuovo record e restituisci un ID (per alcune strutture, un numero di riga) che è stato generato durante l'aggiunta" può essere utilizzato per scopi che altrimenti non potrebbero essere raggiunti in modo efficiente.
supercat

Non è necessario restituire le righe interessate. Il comando dovrebbe sapere quanti saranno interessati. Se non è altro che quel #, dovrebbe tornare indietro e generare un'eccezione poiché è accaduto qualcosa di strano. Pertanto, si tratta di informazioni che al chiamante non interessa davvero (a meno che l'utente non abbia bisogno di conoscere il vero # e non si possa dire dai dati forniti). Ci sono ancora aree grigie come ID generati da DB, stack / code in cui ottenere i dati cambia il suo stato, ma mi piace creare un "CommandQuery" in quegli scenari, quindi nessuno prova a fare qualcosa di "strano".
Daniel Lorenz,

3

Qualunque sia il percorso, camminerai con questo. Non avere valori di ritorno lì per un uso futuro (ad esempio, le funzioni restituiscono sempre true) questo è un terribile spreco di sforzi e non rende il codice più chiaro da leggere. Quando hai un valore di ritorno dovresti sempre usarlo e per poterlo usare dovresti avere almeno 2 possibili valori di ritorno.


+1 Questo non risponde alla domanda, ma è sicuramente un'informazione corretta. Quando si prende una decisione, la decisione dovrebbe essere basata su un'esigenza. Se i chiamanti non trovano utili i dati di ritorno, non dovrebbero essere fatti. E nessuno trova utile nel restituire il 100% delle volte per "prove future". Certo, se il "futuro" è come ... tra un paio di giorni e c'è già un compito per esso, questa è una storia diversa, lol.
Suamere,

1

Scrivevo molte funzioni vuote. Ma da quando ho iniziato l'intero metodo a concatenare il crack, ho iniziato a restituire questo piuttosto che il vuoto - potrei anche lasciare che qualcuno tragga vantaggio dal ritorno perché non puoi fare squat con il vuoto. E se non vogliono farci niente, possono semplicemente ignorarlo.


1
Il concatenamento dei metodi può rendere l'eredità ingombrante e difficile però. Non farei il concatenamento dei metodi a meno che tu non abbia una buona ragione per farlo a parte il non voler rendere i tuoi metodi "nulli".
KallDrexx,

Vero. Ma l'eredità può essere ingombrante e difficile da usare.
Wyatt Barnett,

Guardando un codice sconosciuto che restituisce qualunque cosa (menzionato l'intero oggetto) presta molta attenzione, basta controllare il flusso all'interno e all'esterno dei metodi ..
Indipendente

Immagino non ti sia preso Rubyad un certo punto? Questo è ciò che mi ha introdotto al concatenamento di metodi.
KChaloux,

Un problema che ho con il concatenamento del metodo è che rende meno chiaro se un'istruzione del tipo return Thing.Change1().Change2();si aspetta di cambiare Thinge restituire un riferimento all'originale, o se si prevede che restituisca un riferimento a una cosa nuova che differisce dall'originale, lasciando originale intatto.
supercat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.