Domanda

Ho visto il seguente codice molte volte:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);

    // or
    // throw;

    // or
    // throw ex;
}

Puoi per favore spiegare lo scopo del rilancio di un'eccezione? Sta seguendo un modello / best practice nella gestione delle eccezioni? (Ho letto da qualche parte che si chiama " Caller Inform " pattern?)

È stato utile?

Soluzione

Il rilancio della stessa eccezione è utile se si desidera, per esempio, registrare l'eccezione, ma non gestirla.

Il lancio di una nuova eccezione che avvolge l'eccezione rilevata è utile per l'astrazione. ad esempio, la tua libreria utilizza una libreria di terze parti che genera un'eccezione che i client della tua libreria non dovrebbero conoscere. In tal caso, lo avvolgi in un tipo di eccezione più nativo per la tua libreria e lo lanci invece.

Altri suggerimenti

In realtà c'è una differenza tra

throw new CustomException(ex);

e

throw;

Il secondo conserverà le informazioni sullo stack.

Ma a volte vuoi rendere l'eccezione più "amichevole". al dominio dell'applicazione, anziché consentire a DatabaseException di raggiungere la GUI, aumenterai l'eccezione personalizzata che contiene l'eccezione originale.

Ad esempio:

try
{

}
catch (SqlException ex)
{
    switch  (ex.Number) {
        case 17:
        case 4060:
        case 18456:
           throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
        case 547:
            throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
        default:
            throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
    } 
}

A volte vuoi nascondere o migliorare i dettagli di implementazione di un metodo il livello di astrazione di un problema in modo che sia più significativo per il chiamante di un metodo. Per fare ciò, è possibile intercettare l'eccezione originale e sostituirla un'eccezione personalizzata più adatta a spiegare il problema.

Prendi ad esempio un metodo che carica i dettagli dell'utente richiesto da un file di testo. Il metodo presuppone che esista un file di testo denominato con l'ID dell'utente e un suffisso ".data". Quando quel file in realtà non esiste, non ha molto senso lanciare un FileNotFoundException perché il fatto che i dettagli di ciascun utente siano memorizzati in un file di testo è un dettaglio di implementazione interno al metodo. Quindi questo metodo potrebbe invece racchiudere l'eccezione originale in un'eccezione personalizzata con un messaggio esplicativo.

A differenza del codice che ti viene mostrato, la migliore pratica è che l'eccezione originale debba essere mantenuta caricandola come proprietà InnerException della tua nuova eccezione. Ciò significa che uno sviluppatore può ancora analizzare il problema sottostante, se necessario.

Quando crei un'eccezione personalizzata, ecco un utile elenco di controllo:

• Trova un buon nome che indichi il motivo per cui è stata generata l'eccezione e assicurati che il nome termini con la parola "Eccezione".

• Assicurarsi di implementare i tre costruttori di eccezioni standard.

• Assicurati di contrassegnare la tua eccezione con l'attributo Serializable.

• Assicurarsi di implementare il costruttore di deserializzazione.

• Aggiungi eventuali proprietà personalizzate delle eccezioni che potrebbero aiutare gli sviluppatori a comprendere e gestire meglio la tua eccezione.

• Se si aggiungono proprietà personalizzate, assicurarsi di implementare e sovrascrivere GetObjectData per serializzare le proprietà personalizzate.

• Se si aggiungono proprietà personalizzate, sovrascrivere la proprietà Message in modo da poter aggiungere le proprietà al messaggio di eccezione standard.

• Ricorda di allegare l'eccezione originale usando la proprietà InnerException dell'eccezione personalizzata.

In genere si cattura e si rilancia per uno dei due motivi, a seconda della posizione architettonica del codice all'interno di un'applicazione.

Al centro di un'applicazione in genere catturi e rilasci per tradurre un'eccezione in qualcosa di più significativo. Ad esempio, se si sta scrivendo un livello di accesso ai dati e si utilizzano codici di errore personalizzati con SQL Server, è possibile tradurre SqlException in cose come ObjectNotFoundException. Ciò è utile perché (a) semplifica la gestione da parte dei chiamanti di tipi specifici di eccezioni e (b) perché impedisce i dettagli di implementazione di quel livello, ad esempio il fatto che si sta utilizzando SQL Server per perdite di persistenza in altri livelli, che ti consente di cambiare le cose in futuro più facilmente.

Ai confini delle applicazioni è comune catturare e rilanciare senza tradurre un'eccezione in modo da poterne registrare i dettagli, aiutando nel debug e nella diagnosi di problemi live. Idealmente, si desidera pubblicare un errore da qualche parte che il team operativo può facilmente monitorare (ad esempio il registro eventi) e da qualche parte che fornisce un contesto in cui si è verificata l'eccezione nel flusso di controllo per gli sviluppatori (in genere traccia).

Posso pensare ai seguenti motivi:

  • Mantenendo fisso l'insieme dei tipi di eccezione generati, come parte dell'API, in modo che i chiamanti debbano preoccuparsi solo dell'insieme fisso di eccezioni. In Java, sei praticamente costretto a farlo, a causa del meccanismo delle eccezioni verificato.

  • Aggiunta di alcune informazioni di contesto all'eccezione. Ad esempio, invece di lasciare il nudo "record non trovato" passare dal DB, è possibile catturarlo e aggiungere " ... durante l'elaborazione dell'ordine no XXX, cercando il prodotto YYY " ;.

  • Esecuzione di alcune operazioni di pulizia: chiusura dei file, rollback delle transazioni, liberazione di alcuni handle.

Generalmente il " Do Something " implica sia una migliore spiegazione dell'eccezione (ad esempio, avvolgendola in un'altra eccezione), sia la traccia delle informazioni attraverso una determinata fonte.

Un'altra possibilità è se il tipo di eccezione non è abbastanza informazioni per sapere se un'eccezione deve essere catturata, nel qual caso la cattura e l'esame fornirà ulteriori informazioni.

Questo non vuol dire che il metodo sia usato per ragioni puramente buone, molte volte viene utilizzato quando uno sviluppatore ritiene che le informazioni di tracciamento possano essere necessarie in un momento futuro, nel qual caso si prova a provare lo stile {} catch {throw;} , il che non è affatto utile.

Penso che dipenda da cosa stai cercando di fare con l'eccezione.

Un buon motivo sarebbe quello di registrare prima l'errore nella cattura, e poi lanciarlo nell'interfaccia utente per generare un messaggio di errore amichevole con l'opzione per vedere un più "avanzato / dettagliato" visualizzazione dell'errore, che contiene l'errore originale.

Un altro approccio è un "riprova" approccio, ad esempio, viene mantenuto un conteggio degli errori e, dopo un certo numero di tentativi, questa è l'unica volta in cui l'errore viene inviato nello stack (a volte questo viene fatto per l'accesso al database per le chiamate del database che scadono o per l'accesso ai servizi Web su reti lente ).

Ci saranno molti altri motivi per farlo.

Cordiali saluti, questa è una domanda correlata su ogni tipo di rilancio: Considerazioni sulle prestazioni per generare eccezioni

La mia domanda si concentra su " Why " ridiscutiamo le eccezioni e il suo utilizzo nella strategia di gestione delle eccezioni dell'applicazione.

Fino a quando non ho iniziato a utilizzare EntLib ExceptionBlock, li stavo usando per registrare gli errori prima di lanciarli. È un po 'brutto quando pensi che avrei potuto gestirli a quel punto, ma al momento era meglio farli fallire brutalmente in UAT (dopo averli registrati) piuttosto che coprire un bug flow-on.

Probabilmente l'applicazione rileverà quelle eccezioni rilanciate più in alto nello stack di chiamate e quindi il loro rilancio consente a quel gestore di livello superiore di intercettarle ed elaborarle come appropriato. È abbastanza comune che l'applicazione abbia un gestore di eccezioni di livello superiore che registra o segnala le aspettative.

Un'altra alternativa è che il programmatore era pigro e invece di catturare solo l'insieme di eccezioni che vogliono gestire, hanno catturato tutto e poi rilanciato solo quelli che non sono in grado di gestire.

Come menzionato Rafal, a volte questo viene fatto per convertire un'eccezione controllata in un'eccezione non selezionata o in un'eccezione controllata più adatta per un'API. C'è un esempio qui:

http://radio-weblogs.com/0122027 /stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html

Se osservi le eccezioni come su un modo alternativo per ottenere un risultato del metodo , riproporre un'eccezione è come racchiudere il risultato in un altro oggetto.

E questa è un'operazione comune in un mondo non eccezionale. Di solito ciò accade su un bordo di due livelli dell'applicazione: quando una funzione del livello B chiama una funzione dal livello C , trasforma i C risulta nel modulo interno di B .

A - chiama - > B - chiama - > C

In caso contrario, al livello A che chiama il livello B ci sarà una serie completa di eccezioni JDK da gestire. : -)

Come sottolinea anche la risposta accettata, il livello A potrebbe non essere nemmeno a conoscenza dell'eccezione di C .

Esempio

Livello A , servlet: recupera un'immagine e le sue meta informazioni
Livello B , libreria JPEG: raccoglie i tag DCIM disponibili per analizzare un file JPEG
Livello C , un semplice DB: una classe che legge i record di stringa da un file ad accesso casuale. Alcuni byte sono rotti, quindi genera un'eccezione che dice " impossibile leggere la stringa UTF-8 per il record 'bibliographicCitation' " ;.

Quindi A non capirà il significato di 'bibliographicCitation'. Quindi B dovrebbe tradurre questa eccezione per A in TagsReadingException che avvolge l'originale.

LA RAGIONE PRINCIPALE delle eccezioni di rilancio è quella di lasciare intatto lo stack di chiamate, in modo da poter avere un quadro più completo di ciò che accade e la sequenza di chiamate.

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