Domanda

Esistono buone ragioni per cui è preferibile avere una sola istruzione return in una funzione?

Oppure va bene ritornare da una funzione non appena è logicamente corretto farlo, il che significa che potrebbero esserci molte istruzioni return nella funzione?

È stato utile?

Soluzione

Spesso ho diverse istruzioni da restituire all'inizio di un metodo per situazioni "facili".Ad esempio, questo:

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

...può essere reso più leggibile (IMHO) in questo modo:

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

Quindi sì, penso che sia giusto avere più "punti di uscita" da una funzione/metodo.

Altri suggerimenti

Nessuno ha menzionato o citato Codice completato quindi lo farò.

17.1 restituzione

Ridurre al minimo il numero di resi in ciascuna routine.È più difficile comprendere una routine se, leggendola in fondo, non si ha consapevolezza della possibilità che ritorni da qualche parte sopra.

Usare un ritorno quando migliora la leggibilità.In alcune routine, una volta conosciuta la risposta, è necessario riportarla immediatamente alla routine chiamante.Se la routine è definita in modo tale da non richiedere alcuna pulizia, non restituirla immediatamente significa che sarà necessario scrivere altro codice.

Direi che sarebbe incredibilmente imprudente decidere arbitrariamente contro più punti di uscita poiché ho trovato la tecnica utile nella pratica ancora e ancora, in effetti l'ho fatto spesso refactoring del codice esistente a più punti di uscita per chiarezza.Possiamo confrontare i due approcci in questo modo: -

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

Confronta questo con il codice in cui sono presenti più punti di uscita Sono consentito:-

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

Penso che quest'ultimo sia notevolmente più chiaro.Per quanto ne so, la critica ai punti di uscita multipli è un punto di vista piuttosto arcaico al giorno d'oggi.

Attualmente sto lavorando su una base di codice in cui due delle persone che ci lavorano sottoscrivono ciecamente la teoria del "punto di uscita singolo" e posso dirti che per esperienza è una pratica orribile e orribile.Rende il codice estremamente difficile da mantenere e ti mostrerò perché.

Con la teoria del "punto di uscita singolo", inevitabilmente ti ritroverai con un codice simile a questo:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

Questo non solo rende il codice molto difficile da seguire, ma ora diciamo più tardi che devi tornare indietro e aggiungere un'operazione tra 1 e 2.Devi rientrare quasi tutta la dannata funzione e buona fortuna assicurandoti che tutte le condizioni if/else e le parentesi graffe siano abbinate correttamente.

Questo metodo rende la manutenzione del codice estremamente difficile e soggetta a errori.

Programmazione strutturata dice che dovresti avere solo un'istruzione return per funzione.Questo per limitare la complessità.Molte persone come Martin Fowler sostengono che sia più semplice scrivere funzioni con più istruzioni return.Presenta questo argomento nel classico refactoring libro che ha scritto.Funziona bene se segui gli altri suoi consigli e scrivi piccole funzioni.Sono d'accordo con questo punto di vista e solo i puristi rigorosi della programmazione strutturata aderiscono alle singole istruzioni di ritorno per funzione.

Come nota Kent Beck quando si discute delle clausole di guardia in Modelli di implementazione fare in modo che una routine abbia un unico punto di entrata e di uscita...

"Era quello di prevenire la confusione possibile quando si saltava dentro e fuori da molte posizioni nella stessa routine.Aveva un buon senso se applicato a programmi di linguaggio fortran o di assemblaggio scritti con molti dati globali in cui anche capire quali dichiarazioni sono state eseguite era un duro lavoro ...con metodi piccoli e dati per lo più locali, è inutilmente conservativo."

Trovo che una funzione scritta con clausole di guardia sia molto più facile da seguire rispetto a un lungo gruppo di clausole nidificate if then else dichiarazioni.

In una funzione che non ha effetti collaterali, non c'è una buona ragione per avere più di un singolo ritorno e dovresti scriverli in uno stile funzionale.In un metodo con effetti collaterali, le cose sono più sequenziali (indicizzate nel tempo), quindi scrivi in ​​uno stile imperativo, utilizzando l'istruzione return come comando per interrompere l'esecuzione.

In altre parole, quando possibile, privilegiate questo stile

return a > 0 ?
  positively(a):
  negatively(a);

su questo

if (a > 0)
  return positively(a);
else
  return negatively(a);

Se ti ritrovi a scrivere diversi livelli di condizioni nidificate, probabilmente c'è un modo per rifattorizzarlo, utilizzando ad esempio l'elenco dei predicati.Se scopri che i tuoi se e gli altri sono distanti sintatticamente, potresti voler scomporli in funzioni più piccole.Un blocco condizionale che occupa più di una schermata di testo è difficile da leggere.

Non esiste una regola fissa che si applica a ogni lingua.Qualcosa come avere una singola istruzione return non renderà valido il tuo codice.Ma un buon codice tenderà a permetterti di scrivere le tue funzioni in questo modo.

L'ho visto negli standard di codifica per C++ che erano un postumi di una sbornia da C, come se non avessi RAII o altra gestione automatica della memoria, devi ripulire per ogni ritorno, il che significa taglia e incolla della pulizia o di un goto (logicamente uguale a 'finalmente' nei linguaggi gestiti), entrambi considerati cattiva forma.Se le tue pratiche prevedono l'utilizzo di puntatori e raccolte intelligenti in C++ o in un altro sistema di memoria automatica, allora non c'è una ragione forte per farlo, e diventa tutta una questione di leggibilità e più un giudizio.

Mi appoggio all'idea che le dichiarazioni di ritorno nel file mezzo della funzione sono cattivi.Puoi utilizzare i ritorni per creare alcune clausole di guardia nella parte superiore della funzione e, naturalmente, dire al compilatore cosa restituire alla fine della funzione senza problemi, ma restituisce nel mezzo della funzione può essere facile non notarla e può rendere la funzione più difficile da interpretare.

Esistono buone ragioni per cui è preferibile avere una sola istruzione return in una funzione?

, ci sono:

  • Il singolo punto di uscita offre un luogo eccellente per affermare le tue post-condizioni.
  • Spesso è utile poter inserire un punto di interruzione del debugger sull'unico ritorno alla fine della funzione.
  • Meno rendimenti significano meno complessità.Il codice lineare è generalmente più semplice da comprendere.
  • Se provare a semplificare una funzione in un unico rendimento causa complessità, allora questo è un incentivo a effettuare il refactoring in funzioni più piccole, più generali e più facili da comprendere.
  • Se ti trovi in ​​una lingua senza distruttori o se non usi RAII, un singolo reso riduce il numero di posti che devi ripulire.
  • Alcune lingue richiedono un unico punto di uscita (ad esempio, Pascal ed Eiffel).

La domanda viene spesso posta come una falsa dicotomia tra rendimenti multipli o dichiarazioni if ​​profondamente annidate.C'è quasi sempre una terza soluzione che è molto lineare (senza nidificazione profonda) con un solo punto di uscita.

Aggiornamento:Apparentemente Le linee guida MISRA promuovono l’uscita unica, pure.

Per essere chiari, non sto dicendo che lo sia Sempre sbagliato avere più resi.Ma a fronte di soluzioni altrimenti equivalenti, ci sono molti buoni motivi per preferire quella con un unico rendimento.

Avere un singolo punto di uscita fornisce un vantaggio nel debug, perché consente di impostare un singolo punto di interruzione alla fine di una funzione per vedere quale valore verrà effettivamente restituito.

In generale cerco di avere un solo punto di uscita da una funzione.A volte, tuttavia, così facendo si finisce per creare un corpo della funzione più complesso del necessario, nel qual caso è meglio avere più punti di uscita.Deve davvero essere un "giudizio" basato sulla complessità risultante, ma l'obiettivo dovrebbe essere il minor numero possibile di punti di uscita senza sacrificare la complessità e la comprensibilità.

No perché non viviamo più negli anni '70.Se la tua funzione è abbastanza lunga da rendere più resi un problema, è troppo lunga.

(A parte il fatto che qualsiasi funzione multilinea in una lingua con eccezioni avrà comunque più punti di uscita.)

La mia preferenza sarebbe per l'uscita singola a meno che non complichi davvero le cose.Ho scoperto che in alcuni casi, più punti esistenti possono mascherare altri problemi di progettazione più significativi:

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

Vedendo questo codice, chiederei immediatamente:

  • 'foo' è mai nullo?
  • In tal caso, quanti client di "DoStuff" chiamano mai la funzione con un "foo" nullo?

A seconda delle risposte a queste domande potrebbe essere quello

  1. il controllo è inutile in quanto non è mai vero (es.dovrebbe essere un'affermazione)
  2. il controllo è molto raramente vero e quindi potrebbe essere meglio modificare quelle specifiche funzioni del chiamante poiché probabilmente dovrebbero comunque intraprendere qualche altra azione.

In entrambi i casi precedenti il ​​codice può probabilmente essere rielaborato con un'asserzione per garantire che 'foo' non sia mai nullo e che i chiamanti rilevanti siano cambiati.

Ci sono altri due motivi (specifici, penso, per il codice C++) per cui più esistenze possono effettivamente avere un file negativo simulare.Sono la dimensione del codice e le ottimizzazioni del compilatore.

Un oggetto C++ non POD nell'ambito all'uscita di una funzione verrà chiamato il suo distruttore.Laddove sono presenti più istruzioni return, è possibile che vi siano diversi oggetti nell'ambito e quindi l'elenco dei distruttori da chiamare sarà diverso.Il compilatore deve quindi generare codice per ogni istruzione return:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

Se la dimensione del codice è un problema, potrebbe essere qualcosa che vale la pena evitare.

L'altro problema riguarda "Ottimizzazione del valore restituito con nome" (noto anche come Copy Elision, ISO C++ '03 12.8/15).C++ consente a un'implementazione di saltare la chiamata al costruttore di copie se può:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

Prendendo il codice così com'è, l'oggetto 'a1' viene costruito in 'foo' e quindi il suo costrutto di copia verrà chiamato per costruire 'a2'.Tuttavia, l'elisione della copia consente al compilatore di costruire 'a1' nello stesso posto nello stack di 'a2'.Non è quindi necessario "copiare" l'oggetto al termine della funzione.

Punti di uscita multipli complicano il lavoro del compilatore nel tentativo di rilevarlo, e almeno per una versione relativamente recente di VC++ l'ottimizzazione non ha avuto luogo dove il corpo della funzione aveva più ritorni.Vedere Ottimizzazione del valore restituito denominata in Visual C++ 2005 per ulteriori dettagli.

Avere un unico punto di uscita si riduce Complessità ciclomatica e quindi, in teoria, riduce la probabilità di introdurre bug nel codice quando lo si modifica.La pratica, tuttavia, tende a suggerire che è necessario un approccio più pragmatico.Tendo quindi a mirare ad avere un unico punto di uscita, ma permetto al mio codice di averne diversi se è più leggibile.

Mi sforzo di usarne solo uno return istruzione, poiché in un certo senso genererà un odore di codice.Lasciatemi spiegare:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(Le condizioni sono arbitrarie...)

Più sono le condizioni, più grande diventa la funzione, più difficile sarà leggerla.Quindi, se sei in sintonia con l'odore del codice, te ne renderai conto e vorrai rifattorizzare il codice.Due possibili soluzioni sono:

  • Resi multipli
  • Refactoring in funzioni separate

Resi multipli

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

Funzioni separate

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

Certo, è più lungo e un po' complicato, ma nel processo di refactoring della funzione in questo modo, abbiamo

  • creato una serie di funzioni riutilizzabili,
  • ha reso la funzione più leggibile dall'uomo e
  • il focus delle funzioni è sul motivo per cui i valori sono corretti.

Direi che dovresti averne tutti quelli richiesti, o qualcuno che renda il codice più pulito (come clausole di guardia).

Personalmente non ho mai sentito/visto alcuna "migliore pratica" affermare che dovresti avere una sola istruzione di reso.

Per la maggior parte, tendo a uscire da una funzione il prima possibile in base a un percorso logico (le clausole di guardia ne sono un ottimo esempio).

Credo che i rendimenti multipli siano generalmente positivi (nel codice che scrivo in C#).Lo stile a ritorno singolo è un residuo di C.Ma probabilmente non stai programmando in C.

Non esiste alcuna legge che richieda un solo punto di uscita per un metodo in tutti i linguaggi di programmazione.Alcune persone insistono sulla superiorità di questo stile, e talvolta lo elevano a "regola" o "legge", ma questa convinzione non è supportata da alcuna prova o ricerca.

Più di uno stile di ritorno può essere una cattiva abitudine nel codice C, dove le risorse devono essere deallocate esplicitamente, ma linguaggi come Java, C#, Python o JavaScript che hanno costrutti come garbage collection automatica e try..finally blocchi (e using blocchi in C#) e questo argomento non si applica: in questi linguaggi è molto raro che sia necessaria la deallocazione manuale centralizzata delle risorse.

Ci sono casi in cui un singolo reso è più leggibile e casi in cui non lo è.Verifica se riduce il numero di righe di codice, rende la logica più chiara o riduce il numero di parentesi graffe e rientri o variabili temporanee.

Pertanto, utilizza tutti i rendimenti adatti alla tua sensibilità artistica, perché è un problema di layout e leggibilità, non tecnico.

ne ho parlato questo in modo più approfondito sul mio blog.

Ci sono cose positive da dire sull’avere un unico punto di uscita, così come ci sono cose negative da dire sull’inevitabile "freccia" programmazione che ne risulta.

Se si utilizzano più punti di uscita durante la convalida dell'input o l'allocazione delle risorse, provo a mettere tutte le "uscite di errore" in modo molto visibile nella parte superiore della funzione.

Entrambi i Programmazione spartana articolo della "SSDSLPedia" ed il punto di uscita della singola funzione articolo del "Wiki di Portland Pattern Repository" contiene alcune argomentazioni penetranti al riguardo.Inoltre, ovviamente, c'è questo post da considerare.

Se vuoi davvero un singolo punto di uscita (in qualsiasi linguaggio non abilitato alle eccezioni), ad esempio per rilasciare risorse in un unico posto, trovo che l'attenta applicazione di goto sia buona;guarda ad esempio questo esempio piuttosto artificioso (compresso per risparmiare spazio sullo schermo):

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

Personalmente, in generale, non mi piace la programmazione delle frecce più di quanto non mi piacciano i punti di uscita multipli, sebbene entrambi siano utili se applicati correttamente.La cosa migliore, ovviamente, è strutturare il programma in modo che non richieda nessuno dei due.Suddividere la funzione in più parti di solito aiuta :)

Anche se, così facendo, mi ritrovo comunque con più punti di uscita, come in questo esempio, dove alcune funzioni più grandi sono state suddivise in diverse funzioni più piccole:

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

A seconda del progetto o delle linee guida di codifica, la maggior parte del codice boilerplate potrebbe essere sostituita da macro.Come nota a margine, suddividerlo in questo modo rende le funzioni g0, g1, g2 molto facili da testare individualmente.

Ovviamente, in un linguaggio OO e abilitato alle eccezioni, non utilizzerei istruzioni if ​​del genere (o non utilizzerei affatto, se potessi farla franca con il minimo sforzo), e il codice sarebbe molto più semplice.E non frecciato.E la maggior parte dei rendimenti non definitivi costituirebbero probabilmente delle eccezioni.

In breve;

  • Pochi rendimenti sono meglio di molti rendimenti
  • Più di un ritorno è meglio di frecce enormi e clausole di guardia sono generalmente ok.
  • Le eccezioni potrebbero/dovrebbero probabilmente sostituire la maggior parte delle "clausole di guardia", quando possibile.

Conosci l'adagio - la bellezza è negli occhi di chi guarda.

Alcune persone giurano NetBeans e alcuni da IDEA IntelliJ, alcuni da Pitone e alcuni da PHP.

In alcuni negozi potresti perdere il lavoro se insisti a fare questo:

public void hello()
{
   if (....)
   {
      ....
   }
}

La domanda riguarda la visibilità e la manutenibilità.

Sono dedito all'uso dell'algebra booleana per ridurre e semplificare la logica e l'uso delle macchine a stati.Tuttavia, c'erano colleghi del passato che ritenevano che il mio utilizzo di "tecniche matematiche" nella codifica fosse inadatto, perché non sarebbe visibile e gestibile.E sarebbe una cattiva pratica.Mi dispiace gente, le tecniche che utilizzo sono per me molto visibili e gestibili, perché quando torno al codice sei mesi dopo, capirei il codice chiaramente piuttosto che vedere un pasticcio di proverbiali spaghetti.

Ehi amico (come diceva un ex cliente) fai quello che vuoi finché sai come aggiustarlo quando ho bisogno che tu lo aggiusti.

Ricordo che 20 anni fa un mio collega fu licenziato per aver assunto quello che oggi si chiamerebbe sviluppo agile strategia.Aveva un meticoloso piano incrementale.Ma il suo manager gli stava urlando: "Non puoi rilasciare funzionalità in modo incrementale agli utenti!Devi restare con il cascata." La sua risposta al manager fu che lo sviluppo incrementale sarebbe stato più preciso rispetto alle esigenze del cliente.Credeva nello sviluppo in base alle esigenze dei clienti, ma il manager credeva nella codifica in base alle "requisizioni del cliente".

Spesso siamo colpevoli di aver violato la normalizzazione dei dati, MVP E MVC confini.Mettiamo in linea invece di costruire una funzione.Prendiamo scorciatoie.

Personalmente, credo che PHP sia una cattiva pratica, ma cosa ne so.Tutti gli argomenti teorici si riducono al tentativo di soddisfare una serie di regole

Qualità = precisione, manutenibilità e redditività.

Tutte le altre regole passano in secondo piano.E ovviamente questa regola non viene mai meno:

La pigrizia è la virtù di un buon programmatore.

Io propendo all'utilizzo di clausole di guardia per rientrare anticipatamente e altrimenti uscire alla fine di un metodo.La regola di entrata e uscita singola ha un significato storico ed è stata particolarmente utile quando si ha a che fare con codice legacy che veniva eseguito su 10 pagine A4 per un singolo metodo C++ con più resi (e molti difetti).Più recentemente, la buona pratica accettata è quella di mantenere i metodi piccoli, il che rende le uscite multiple meno un ostacolo alla comprensione.Nel seguente esempio di Kronoz copiato da sopra, la domanda è cosa accade in //Resto del codice...?:

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

Mi rendo conto che l'esempio è alquanto artificioso, ma sarei tentato di rifattorizzare il file per ciascuno entrare in un'istruzione LINQ che potrebbe quindi essere considerata una clausola di guardia.Ancora una volta, in un esempio artificioso l'intento del codice non è evidente e alcunefunzioni() potrebbe avere qualche altro effetto collaterale o il risultato potrebbe essere utilizzato nel file // Resto del codice....

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

Fornendo la seguente funzione di refactoring:

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}

Una buona ragione che mi viene in mente è per la manutenzione del codice:hai un unico punto di uscita.Se vuoi cambiare il formato del risultato,..., è semplicemente molto più semplice da implementare.Inoltre, per il debug, puoi semplicemente inserire un punto di interruzione lì :)

Detto questo, una volta ho dovuto lavorare in una biblioteca dove gli standard di codifica imponevano "un'istruzione di ritorno per funzione", e l'ho trovato piuttosto difficile.Scrivo molto codice per calcoli numerici e spesso ci sono "casi speciali", quindi il codice si è rivelato piuttosto difficile da seguire...

Punti di uscita multipli vanno bene per funzioni sufficientemente piccole, ovvero una funzione che può essere visualizzata su una lunghezza dello schermo nella sua interezza.Se anche una funzione lunga include più punti di uscita, è segno che la funzione può essere ulteriormente suddivisa.

Detto questo evito le funzioni di uscita multipla a meno che non sia assolutamente necessario.Ho provato dolore per i bug dovuti a qualche ritorno vagante in qualche linea oscura in funzioni più complesse.

Ho lavorato con terribili standard di codifica che ti imponevano un unico percorso di uscita e il risultato sono quasi sempre spaghetti non strutturati se la funzione è tutt'altro che banale: ti ritrovi con molte interruzioni e continue che ti intralciano.

Il singolo punto di uscita, a parità di altre condizioni, rende il codice molto più leggibile.Ma c'è un problema:costruzione popolare

resulttype res;
if if if...
return res;

è un falso, "res=" non è molto meglio di "return".Ha una singola istruzione return, ma più punti in cui la funzione termina effettivamente.

Se hai una funzione con più ritorni a capo (o "res="s), spesso è una buona idea suddividerla in diverse funzioni più piccole con un singolo punto di uscita.

La mia politica abituale è di avere una sola istruzione return alla fine di una funzione, a meno che la complessità del codice non venga notevolmente ridotta aggiungendone altre.In effetti, sono piuttosto un fan di Eiffel, che applica l'unica regola di ritorno non avendo alcuna istruzione di ritorno (c'è solo una variabile "risultato" creata automaticamente in cui inserire il risultato).

Ci sono certamente casi in cui il codice può essere reso più chiaro con più resi rispetto alla versione ovvia senza di essi.Si potrebbe sostenere che sono necessarie ulteriori rielaborazioni se si ha una funzione troppo complessa per essere comprensibile senza più istruzioni return, ma a volte è bene essere pragmatici su queste cose.

Se alla fine ottieni più di qualche reso, potrebbe esserci qualcosa di sbagliato nel tuo codice.Altrimenti sarei d'accordo sul fatto che a volte è bello poter tornare da più punti in una subroutine, soprattutto quando rende il codice più pulito.

Perl6:Cattivo esempio

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

sarebbe meglio scrivere così

Perl6:Buon esempio

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

Nota che questo era solo un rapido esempio

Voto a favore della restituzione unica alla fine come linea guida.Questo aiuta a gestione comune della pulizia del codice ...Ad esempio, dai un'occhiata al seguente codice ...

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

Questa è probabilmente una prospettiva insolita, ma penso che chiunque creda che siano da favorire più istruzioni di ritorno non abbia mai dovuto utilizzare un debugger su un microprocessore che supporta solo 4 breakpoint hardware.;-)

Sebbene i problemi del "codice freccia" siano completamente corretti, un problema che sembra risolversi quando si utilizzano più istruzioni return è nella situazione in cui si utilizza un debugger.Non hai una posizione comoda per inserire un punto di interruzione per garantire che vedrai l'uscita e quindi la condizione di ritorno.

Più istruzioni return hai in una funzione, maggiore sarà la complessità di quel metodo.Se ti ritrovi a chiederti se hai troppe istruzioni return, potresti chiederti se hai troppe righe di codice in quella funzione.

Ma no, non c'è niente di sbagliato con una/molte istruzioni di ritorno.In alcuni linguaggi è una pratica migliore (C++) che in altri (C).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top