Penso che @JackAidley abbia già detto l'essenza , ma lasciami formularlo in questo modo:
senza eccezioni (ad es. C)
Nel flusso di codice regolare, hai:
if (condition) {
statement;
} else if (less_likely_condition) {
less_likely_statement;
} else {
least_likely_statement;
}
more_statements;
Nel caso "errore in anticipo", il tuo codice legge improvvisamente:
/* demonstration example, do NOT code like this */
if (condition) {
statement;
} else {
error_handling;
return;
}
Se noti questo modello - a return
in un else
(o anche if
) blocco, rielaboralo immediatamente in modo che il codice in questione non abbia un else
blocco:
/* only code like this at University, to please structured programming professors */
function foo {
if (condition) {
lots_of_statements;
}
return;
}
Nel mondo reale…
/* code like this instead */
if (!condition) {
error_handling;
return;
}
lots_of_statements;
Questo evita l'annidamento troppo profondo e soddisfa il caso "break out early" (aiuta a mantenere la mente - e il flusso del codice - pulito) e non viola il "mettere la cosa più probabile nella if
parte" perché semplicemente non c'è else
parte .
C
e pulizia
Ispirato da una risposta a una domanda simile (che ha sbagliato), ecco come eseguire la pulizia con C. Qui puoi usare uno o due punti di uscita, eccone uno per due punti di uscita:
struct foo *
alloc_and_init(size_t arg1, int arg2)
{
struct foo *res;
if (!(res = calloc(sizeof(struct foo), 1)))
return (NULL);
if (foo_init1(res, arg1))
goto err;
res.arg1_inited = true;
if (foo_init2(&(res->blah), arg2))
goto err;
foo_init_complete(res);
return (res);
err:
/* safe because we use calloc and false == 0 */
if (res.arg1_inited)
foo_dispose1(res);
free(res);
return (NULL);
}
Puoi comprimerli in un punto di uscita se c'è meno pulizia da fare:
char *
NULL_safe_strdup(const char *arg)
{
char *res = NULL;
if (arg == NULL)
goto out;
/* imagine more lines here */
res = strdup(arg);
out:
return (res);
}
Questo uso di goto
va benissimo, se puoi affrontarlo; il consiglio di smettere di usare goto
è rivolto a persone che non possono ancora decidere da sole se un uso è buono, accettabile, cattivo, codice spaghetti o qualcos'altro.
eccezioni
Quanto sopra parla di lingue senza eccezioni, che preferisco di gran lunga me stesso (posso usare la gestione degli errori espliciti molto meglio e con molta meno sorpresa). Per citare igli:
<igli> exceptions: a truly awful implementation of quite a nice idea.
<igli> just about the worst way you could do something like that, afaic.
<igli> it's like anti-design.
<mirabilos> that too… may I quote you on that?
<igli> sure, tho i doubt anyone will listen ;)
Ma ecco un suggerimento su come farlo bene in una lingua con eccezioni e quando vuoi usarli bene:
errore restituito a fronte di eccezioni
È possibile sostituire la maggior parte dei primi return
con l'eccezione. Tuttavia , il normale flusso del programma, ovvero qualsiasi flusso di codice in cui il programma non ha riscontrato, beh, un'eccezione ... una condizione di errore o qualcosa del genere, non deve sollevare eccezioni.
Ciò significa che…
# this page is only available to logged-in users
if not isLoggedIn():
# this is Python 2.5 style; insert your favourite raise/throw here
raise "eh?"
... va bene, ma ...
/* do not code like this! */
try {
openFile(xyz, "rw");
} catch (LockedException e) {
return "file is locked";
}
closeFile(xyz);
return "file is not locked";
… non è. Fondamentalmente, un'eccezione non è un elemento di flusso di controllo . Questo rende anche Operations strano per te ("quei programmatori Java ™ ci dicono sempre che queste eccezioni sono normali") e può ostacolare il debug (es. Dire all'IDE di interrompere qualsiasi eccezione). Le eccezioni spesso richiedono che l'ambiente di runtime rilasci lo stack per produrre traceback, ecc. Probabilmente ci sono più ragioni per evitarlo.
Questo si riduce a: in un linguaggio che supporta le eccezioni, usa tutto ciò che corrisponde alla logica e allo stile esistenti e ti sembra naturale. Se scrivi qualcosa da zero, concorda presto. Se scrivi una libreria da zero, pensa ai tuoi consumatori. (Non usare mai, abort()
neanche in una biblioteca ...) Ma qualunque cosa tu faccia, non fai, come regola generale, un'eccezione se l'operazione continua (più o meno) normalmente dopo di essa.
consiglio generale eccezioni
Cerca di ottenere prima tutto l'utilizzo in programma delle eccezioni concordate da tutto il team di sviluppo. Fondamentalmente, pianificali. Non usarli in abbondanza. A volte, anche in C ++, Java ™, Python, un ritorno dell'errore è migliore. A volte non lo è; usali con il pensiero.