Utilizzo efficiente del blocco try / catch?


21

I blocchi catch dovrebbero essere usati per scrivere la logica, ad esempio gestire il controllo del flusso, ecc.? O semplicemente per generare eccezioni? Influisce sull'efficienza o sulla manutenibilità del codice?

Quali sono gli effetti collaterali (se ce ne sono) della scrittura della logica nel blocco catch?

MODIFICARE:

Ho visto una classe SDK Java in cui hanno scritto la logica all'interno del blocco catch. Ad esempio (frammento preso dalla java.lang.Integerclasse):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Stavo attraversando un tutorial in cui lo considerano un vantaggio di scrivere la logica di casi eccezionali all'interno delle eccezioni:

Le eccezioni ti consentono di scrivere il flusso principale del tuo codice e di trattare casi eccezionali altrove.

Qualche guida specifica su quando scrivere la logica nel blocco catch e quando non farlo?


12
@Coder Penso che tu abbia sovradimensionato un po '. Soprattutto perché con molte delle API esistenti non puoi evitarlo.
CodesInChaos

8
@Coder: in Java le eccezioni sono spesso utilizzate come meccanismo per segnalare problemi che fanno parte del flusso di codice legittimo. Ad esempio, se si tenta di aprire un file e non riesce, la libreria Java lo dice lanciando un'eccezione, perché le API che portano all'apertura di un file non hanno altri meccanismi per restituire un errore.
JeremyP,

4
@Coder Dipende. Penso che quando si esegue l'IO o quando si analizzano dati complessi, che sono quasi sempre formattati correttamente, generare un'eccezione per indicare un errore rende il codice molto più pulito.
CodesInChaos

1
@Coded Sì, l'esecuzione è lì per il metodo per dirti che non è stato in grado di fare ciò che gli è stato chiesto. Ma le eccezioni non dovrebbero essere usate per il controllo del flusso, motivo per cui C # ha anche un metodo int. TryParse che non genera.
Andy,

1
@Coder: Questa è spazzatura totale. Cose che sono eccezionali su un livello di codice potrebbero non essere eccezionali su un altro oppure potresti, ad esempio, presentare o aggiungere informazioni di errore o di debug prima di continuare.
DeadMG

Risposte:


46

L'esempio che citi è dovuto alla cattiva progettazione dell'API (non esiste un modo pulito per verificare se una stringa è un numero intero valido se non quello di provare ad analizzarla e catturare l'eccezione).

A livello tecnico, lancio e prova / cattura sono costrutti di flusso di controllo che consentono di saltare nello stack di chiamate, niente di più e niente di meno. Saltare lo stack delle chiamate in modo implicito collega il codice che non è ravvicinato nell'origine, il che è negativo per la manutenibilità . Quindi dovrebbe essere usato solo quando è necessario farlo e le alternative sono anche peggio. Il caso ampiamente accettato in cui le alternative sono peggiori è la gestione degli errori (codici di ritorno speciali che devono essere controllati e passati manualmente ogni livello dello stack di chiamate).

Se hai un caso in cui le alternative sono peggiori (e le hai davvero prese in considerazione tutte attentamente), direi che usare il tiro e provare / catturare per il flusso di controllo va bene. Il dogma non è un buon sostituto del giudizio.


1
+1, buoni punti fatti. Penso che alcune elaborazioni possano essere valide nella cattura come la preparazione di un messaggio di errore e la registrazione degli errori. La liberazione di risorse costose come connessioni db e riferimenti COM (.NET) potrebbe essere aggiunta al blocco Infine.
NoChance,

@EmmadKareem Ho pensato di liberare risorse, ma di solito devi liberarle comunque ad un certo punto anche senza situazione di errore. Quindi potresti ripetere te stesso. La preparazione di un messaggio di registro è ovviamente accettabile.
scarfridge

@MichaelBorgwardt Penso che il design dell'API non sia così scarso (anche se potrebbe essere migliore). Cercare di analizzare una stringa non valida è chiaramente una condizione di errore e quindi esattamente il "caso ampiamente accettato" che hai citato. D'accordo, un metodo per determinare se una stringa è sintatticamente il numero corretto è utile (qui è dove potrebbe essere migliore). Tuttavia, forzare tutti a chiamare questo metodo prima dell'effettivo analisi non è possibile e dobbiamo gestire l'errore. Mischiare il risultato effettivo e il codice di errore è scomodo e quindi si genererebbe comunque l'eccezione. Ma forse un'eccezione di runtime.
scarfridge

3
+1 per l'ultima frase.
FrustratedWithFormsDesigner

2
@scarfridge: sono completamente d'accordo con te :) L'assenza di un metodo isInteger (String) di ritorno booleano è tutto ciò che intendevo con "progettazione API scadente"
Michael Borgwardt

9

Questo è qualcosa che tende a dipendere dal linguaggio e dal paradigma.

Gran parte del mio lavoro è svolto in Java (e talvolta in C ++). La tendenza è quella di utilizzare le eccezioni solo per condizioni eccezionali. Ci sono alcune domande su Stack Overflow sull'overhead delle prestazioni delle eccezioni in Java e, come puoi vedere, non è esattamente trascurabile. Vi sono anche altre preoccupazioni, come la leggibilità e la manutenibilità del codice. Se utilizzate correttamente, le eccezioni possono delegare la gestione degli errori ai componenti appropriati.

Tuttavia, in Python, l'idea che sia più facile chiedere perdono che regni l'autorizzazione. Nella comunità Python, questo è noto come EAFP , in contrasto con l' approccio "look before you jump " ( LBYL ) nei linguaggi in stile C.


2
+1 per menzionare l'overhead con eccezioni. Faccio .NET e (almeno sulla mia macchina) potrei persino sentire quando un'eccezione sta per accadere per la durata in alcuni casi rispetto ad altre normali operazioni.
NoChance,

2
@EmmadKareem Solo se è collegato un debugger. Puoi lanciarne diverse migliaia al secondo senza un debugger.
Codici A Caos il

@CodeInChaos, hai ragione. Come faccio a sapere, scrivo un codice così pulito che non ne ottengo mai in fase di esecuzione :)
NoChance

@EmmadKareem A seconda di come si scrive un'applicazione di rete, gli errori di connessione o protocollo vengono gestiti con un'eccezione. Le eccezioni devono essere ragionevolmente veloci in tale applicazione per evitare banali attacchi DoS.
CodesInChaos

@CodeInChaos, questo è buono a sapersi. Stavo scherzando nel mio ultimo commento.
NoChance,

9

Questo non è il modo migliore di pensare ai blocchi try / catch. Il modo di pensare ai blocchi try / catch è questo: si è verificato un errore, dove è il posto migliore per gestire QUESTO errore specifico. Questa potrebbe essere la riga di codice successiva, potrebbe essere di 20 livelli nella catena di chiamate. Ovunque sia, ecco dove dovrebbe essere.

Cosa fa il blocco catch dipende dall'errore e cosa puoi fare al riguardo. A volte può semplicemente essere ignorato (impossibile eliminare un file di memoria virtuale senza dati importanti), a volte verrà utilizzato per impostare un valore di ritorno delle funzioni su true o false (metodo int parse di java), a volte si esce dal programma . Tutto ciò dipende dall'errore.

La parte importante da capire è: catch block == So come affrontarlo.


6

I blocchi di cattura dovrebbero essere usati solo per gestire le eccezioni, nient'altro e mai e poi mai per il flusso di controllo. In qualsiasi situazione in cui si desidera utilizzare le eccezioni per gestire il flusso, è meglio verificare le condizioni preliminari.

La gestione del flusso con eccezioni è lenta e semanticamente errata.


4
So cosa stai dicendo, ma vale la pena notare che tutto ciò che fa eccezione è gestire il flusso del programma - solo che dovrebbe essere usato solo per controllare un flusso di errori eccezionale ...
Max

Non ci sono linee guida specifiche su quando scrivere la logica nel blocco catch e quando non farlo?
HashimR,

4
I parser numerici Java e i formattatori di testo sono famosi esempi di utilizzo problematico delle eccezioni: l'unico modo ragionevole per verificare una condizione preliminare (che una stringa rappresenti un numero analizzabile) è provare ad analizzarlo e, in caso contrario, quali sono le tue scelte in pratica? Devi catturare l'eccezione e gestirne il flusso, indipendentemente dal fatto che sia considerato semanticamente errato.
Joonas Pulakka,

@JoonasPulakka Penso che il tuo commento wrt Numero parser e formattatori di testo si qualifichi come una risposta a grandezza naturale a questa domanda
moscerino

5

I blocchi catch dovrebbero essere usati per scrivere la logica, ad esempio gestire il controllo del flusso, ecc.? O semplicemente per generare eccezioni? Influisce sull'efficienza o sulla manutenibilità del codice?

Innanzitutto, dimentica l'idea che "le eccezioni dovrebbero essere utilizzate per condizioni eccezionali". Smetti anche di preoccuparti dell'efficienza, finché non hai un codice che funziona in modo inaccettabile e non hai misurato per sapere dove si trova il problema.

Il codice è più facile da capire quando le azioni seguono in una sequenza semplice, senza condizionali. Le eccezioni migliorano la manutenibilità rimuovendo il controllo degli errori dal flusso normale. Non importa se l'esecuzione segue il flusso normale il 99,9% delle volte o il 50% delle volte o il 20% delle volte.

Generare un'eccezione quando una funzione non è in grado di restituire un valore, quando una procedura non è in grado di completare l'azione prevista o quando un costruttore non è in grado di produrre un oggetto utilizzabile. Ciò consente ai programmatori di assumere che una funzione restituisca sempre un risultato utilizzabile, una procedura completa sempre l'azione richiesta, un oggetto costruito è sempre in uno stato utilizzabile.

Il problema più grande che vedo con il codice di gestione delle eccezioni è che i programmatori scrivono blocchi try / catch quando non dovrebbero. Ad esempio, nella maggior parte delle applicazioni Web, se una richiesta del database genera un'eccezione, non è possibile eseguire alcuna operazione nel controller. È sufficiente una clausola di cattura generica al massimo livello. Quindi il codice del controller può tranquillamente ignorare la possibilità che il disco si sia arrestato in modo anomalo o che il database sia offline o altro.


1

I blocchi di cattura non devono essere utilizzati per scrivere la logica del codice. Dovrebbero essere utilizzati solo per la gestione degli errori. Un esempio è (1) ripulire qualsiasi risorsa allocata, (2) stampare un messaggio utile e (3) uscire con grazia.

È vero che le eccezioni possono essere abusate e causare gotoeffetti indesiderati . Ma ciò non li rende inutili. Se utilizzati correttamente , possono migliorare molti aspetti del codice.

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.