Domanda

Analizzando parte del codice legacy con FXCop, mi è venuto in mente se è davvero così brutto rilevare un errore di eccezione generale all'interno di un blocco try o se stai cercando un'eccezione specifica.Pensieri su una cartolina, per favore.

È stato utile?

Soluzione

Ovviamente questa è una di quelle domande per cui l'unica vera risposta è "dipende".

La cosa principale da cui dipende è dove stai rilevando l'eccezione.In generale le librerie dovrebbero essere più conservatrici nell'intercettare le eccezioni mentre al livello più alto del programma (ad es.nel tuo metodo principale o nella parte superiore del metodo di azione in un controller, ecc.) puoi essere più liberale con ciò che catturi.

La ragione di ciò è che ad es.non vuoi catturare tutte le eccezioni in una libreria perché potresti mascherare problemi che non hanno nulla a che fare con la tua libreria, come "OutOfMemoryException" che preferiresti davvero emergesse in modo che l'utente possa essere avvisato, ecc.D'altra parte, se stai parlando di catturare eccezioni all'interno del tuo metodo main() che cattura l'eccezione, la visualizza e poi esce...beh, probabilmente è sicuro cogliere qualsiasi eccezione qui.

La regola più importante per catturare tutte le eccezioni è che non dovresti mai ingoiare tutte le eccezioni in silenzio...per esempio.qualcosa del genere in Java:

try { 
    something(); 
} catch (Exception ex) {}

o questo in Python:

try:
    something()
except:
    pass

Perché questi possono essere alcuni dei problemi più difficili da rintracciare.

Una buona regola pratica è che dovresti cogliere solo le eccezioni che puoi gestire adeguatamente da solo.Se non riesci a gestire completamente l'eccezione, dovresti lasciarla emergere a qualcuno che può farlo.

Altri suggerimenti

A meno che tu non stia eseguendo la registrazione e la pulizia del codice nel front-end della tua applicazione, penso che sia negativo catturare tutte le eccezioni.

La mia regola pratica di base è catturare tutte le eccezioni previste e qualsiasi altra cosa è un bug.

Se prendi tutto e prosegui, è un po' come mettere un cerotto sulla spia del cruscotto dell'auto.Non lo vedi più, ma non significa che sia tutto ok.

SÌ!(eccetto nella parte "superiore" della tua domanda)

Rilevando un'eccezione e consentendo all'esecuzione del codice di continuare, dichiari di sapere come gestire, aggirare o risolvere un particolare problema.Stai affermando che è così una situazione recuperabile.Rilevare un'eccezione o SystemException significa rilevare problemi come errori di I/O, errori di rete, errori di memoria insufficiente, errori di codice mancante, dereferenziazione di puntatori null e simili.È una bugia dire che puoi affrontarli.

In un'applicazione ben organizzata, questi problemi irreversibili dovrebbero essere gestiti in alto nello stack.

Inoltre, man mano che il codice si evolve, non vuoi che la tua funzione rilevi una nuova eccezione aggiunta in futuro a un metodo chiamato.

Secondo me dovresti cogliere tutte le eccezioni tu aspettarsi, ma questa regola si applica a tutto tranne che alla logica dell'interfaccia.In tutto lo stack di chiamate dovresti probabilmente creare un modo per catturare tutte le eccezioni, eseguire alcune registrazioni/fornire feedback agli utenti e, se necessario e possibile, chiudere con garbo.

Non c'è niente di peggio di un'applicazione che si blocca con uno stacktrace ostile all'utente scaricato sullo schermo.Non solo fornisce informazioni (forse indesiderate) sul tuo codice, ma confonde anche l'utente finale e talvolta lo spaventa persino spingendolo verso un'applicazione concorrente.

Ci sono state molte discussioni filosofiche (più simili ad argomenti) su questo problema.Personalmente, credo che la cosa peggiore che puoi fare sia ingoiare le eccezioni.La cosa peggiore è lasciare che un'eccezione emerga in superficie dove l'utente ottiene uno schermo brutto pieno di chiacchiere tecniche.

Il punto credo sia duplice.

In primo luogo, se non sai quale eccezione si è verificata, come puoi sperare di risolverla.Se prevedi che un utente possa digitare un nome file in modo errato, puoi aspettarti una FileNotFoundException e dire all'utente di riprovare.Se lo stesso codice generasse una NullReferenceException e tu dicessi semplicemente all'utente di riprovare, non saprebbero cosa è successo.

In secondo luogo, le linee guida FxCop si concentrano sul codice Library/Framework: non tutte le loro regole sono progettate per essere applicabili ai siti Web EXE o ASP.Net.Quindi avere un gestore di eccezioni globale che registrerà tutte le eccezioni e uscirà correttamente dall'applicazione è una buona cosa da avere.

Il problema nel catturare tutte le eccezioni è che potresti catturare quelle che non ti aspetti, o addirittura quelle che dovresti non essere catturato.Il fatto è che un'eccezione di qualsiasi tipo indica che qualcosa è andato storto e devi risolverlo prima di continuare altrimenti potresti ritrovarti con problemi di integrità dei dati e altri bug che non sono così facili da rintracciare.

Per fare un esempio, in un progetto ho implementato un tipo di eccezione chiamato CriticalException.Ciò indica una condizione di errore che richiede l'intervento degli sviluppatori e/o del personale amministrativo, altrimenti i clienti riceveranno fatture errate o potrebbero verificarsi altri problemi di integrità dei dati.Può essere utilizzato anche in altri casi simili quando la semplice registrazione dell'eccezione non è sufficiente ed è necessario inviare un avviso tramite posta elettronica.

Un altro sviluppatore che non comprendeva correttamente il concetto di eccezioni ha quindi inserito del codice che potrebbe potenzialmente generare questa eccezione in un blocco generico try...catch che scarta tutte le eccezioni.Fortunatamente l'ho notato, ma avrebbe potuto causare seri problemi, soprattutto perché il caso d'angolo "molto raro" che avrebbe dovuto rilevare si è rivelato molto più comune di quanto mi aspettassi.

Quindi, in generale, rilevare eccezioni generiche è negativo a meno che tu non sia sicuro al 100% di saperlo esattamente quali tipi di eccezioni verranno lanciate e in quali circostanze.In caso di dubbio, lasciali invece salire al gestore delle eccezioni di livello superiore.

Una regola simile qui è non generare mai eccezioni di tipo System.Exception.Tu (o un altro sviluppatore) potresti voler catturare la tua eccezione specifica più in alto nello stack di chiamate lasciando passare gli altri.

(C’è però un punto da notare.In .NET 2.0, se un thread rileva eccezioni non rilevate, scarica l'intero dominio dell'app.Quindi dovresti racchiudere il corpo principale di un thread in un blocco generico try...catch e passare qualsiasi eccezione catturata lì al tuo codice di gestione delle eccezioni globale.)

Bene, non vedo alcuna differenza tra catturare un'eccezione generale o una specifica, tranne che quando hai più blocchi catch, puoi reagire in modo diverso a seconda di quale sia l'eccezione.

In conclusione, li catturerai entrambi IOException E NullPointerException con un generico Exception, ma il modo in cui il tuo programma dovrebbe reagire è probabilmente diverso.

Mi piacerebbe interpretare l'avvocato del diavolo per catturare Exception, registrarla e rilanciarla.Ciò può essere necessario se, ad esempio, ti trovi da qualche parte nel codice e si verifica un'eccezione imprevista, puoi rilevarla, registrare informazioni sullo stato significative che non sarebbero disponibili in una semplice analisi dello stack e quindi riproporle ai livelli superiori per affrontare.

Esistono due casi d'uso completamente diversi.Il primo è quello a cui pensa la maggior parte delle persone, ovvero mettere un try/catch attorno ad alcune operazioni che richiedono un'eccezione controllata.Questo non dovrebbe essere in alcun modo un problema.

Il secondo, tuttavia, è impedire che il programma si interrompa quando potrebbe continuare.Questi casi sono:

  • La parte superiore di tutti i thread (per impostazione predefinita, le eccezioni svaniranno senza lasciare traccia!)
  • All'interno di un ciclo di elaborazione principale che prevedi non uscirà mai
  • All'interno di un Loop che elabora un elenco di oggetti in cui un errore non dovrebbe fermarne altri
  • Inizio del thread "principale": qui potresti controllare un arresto anomalo, ad esempio scaricare alcuni dati su stdout quando esaurisci la memoria.
  • Se hai un "Runner" che esegue il codice (ad esempio, se qualcuno ti aggiunge un ascoltatore e tu chiami l'ascoltatore), quando esegui il codice dovresti catturare un'eccezione per registrare il problema e consentirti di continuare a notificare altri ascoltatori.

In questi casi vuoi SEMPRE catturare un'eccezione (forse anche lanciabile a volte) per catturare errori di programmazione/imprevisti, registrarli e continuare.

Penso che una buona linea guida sia quella di catturare solo eccezioni specifiche all'interno di un framework (in modo che l'applicazione host possa gestire casi limite come il riempimento del disco, ecc.), ma non vedo perché non dovremmo essere in grado di catturare tutto eccezioni dal nostro codice applicativo.Molto semplicemente ci sono momenti in cui non vuoi che l'app si blocchi, non importa cosa potrebbe andare storto.

Nella maggior parte dei casi non è necessario rilevare un'eccezione generale.Naturalmente ci sono situazioni in cui non hai scelta, ma in questo caso penso che sia meglio verificare perché è necessario coglierlo.Forse c'è qualcosa che non va nel tuo design.

Cogliendo un'eccezione generale, mi sembra come tenere un candelotto di dinamite all'interno di un edificio in fiamme e spegnere la spoletta.Aiuta per un po', ma la dinamite esploderà comunque dopo un po'.

Naturalmente potrebbero esserci situazioni in cui è necessario rilevare un'eccezione generale, ma solo a scopo di debug.Errori e bug dovrebbero essere corretti, non nascosti.

Per la mia lezione IabManager, che ho utilizzato con la fatturazione in-app (dall'esempio TrivialDrive online), ho notato che a volte dovevo gestire molte eccezioni.È arrivato al punto in cui era imprevedibile.

Mi sono reso conto che, fintanto che avessi smesso di provare a consumare un prodotto in-app dopo che si è verificata un'eccezione, che è il momento in cui si verificherebbe la maggior parte delle eccezioni (nel consumo, anziché nell'acquisto), sarei stato al sicuro.

Ho appena cambiato tutte le eccezioni in un'eccezione generale e ora non devo preoccuparmi che vengano generate altre eccezioni casuali e imprevedibili.

Prima:

    catch (final RemoteException exc)
    {
        exc.printStackTrace();
    }
    catch (final IntentSender.SendIntentException exc)
    {
        exc.printStackTrace();
    }
    catch (final IabHelper.IabAsyncInProgressException exc)
    {
        exc.printStackTrace();
    }
    catch (final NullPointerException exc)
    {
        exc.printStackTrace();
    }
    catch (final IllegalStateException exc)
    {
        exc.printStackTrace();
    }

Dopo:

    catch (final Exception exc)
    {
        exc.printStackTrace();
    }

Opinione impopolare:Non proprio.

Individua tutti gli errori da cui puoi recuperare in modo significativo.A volte è tutto.

Nella mia esperienza, conta di più Dove l'eccezione proviene da quale eccezione viene effettivamente lanciata.Se mantieni le tue eccezioni in spazi ristretti, di solito non ingoierai nulla che altrimenti sarebbe utile.La maggior parte delle informazioni codificate nel tipo di errore sono informazioni accessorie, quindi generalmente si finisce per rilevarle efficacemente Tutto di loro comunque (ma ora devi cercare la documentazione API per ottenere il set totale di possibili eccezioni).

Tieni presente che alcune eccezioni dovrebbero emergere in cima in quasi tutti i casi, come quello di Python KeyboardInterrupt E SystemExit.Fortunatamente per Python, questi sono mantenuti in un ramo separato della gerarchia delle eccezioni, quindi puoi lasciarli emergere catturandoli Exception.Una gerarchia di eccezioni ben progettata rende questo tipo di cose davvero semplice.

Il momento in cui l'individuazione di eccezioni generali causerà seri problemi è quando si ha a che fare con risorse che devono essere ripulite (magari in un finally clausola), dal momento che un gestore catch-all può facilmente perdere questo genere di cose.Fortunatamente questo non è davvero un problema per le lingue con defer, costrutti come quelli di Python with, o RAII in C++ e Rust.

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