Quando lanciare un'eccezione?


435

Sono state create eccezioni per ogni condizione che la mia applicazione non prevede. UserNameNotValidException, PasswordNotCorrectExceptionEcc

Tuttavia mi è stato detto che non avrei dovuto creare eccezioni per tali condizioni. Nel mio UML quelle SONO eccezioni al flusso principale, quindi perché non dovrebbe essere un'eccezione?

Qualche guida o best practice per la creazione di eccezioni?


59
Riaprire, questa è una domanda molto ragionevole e valida. Qualsiasi domanda comporta una certa quantità di opinioni, ma in questo caso sospetto che si tratti di "buone pratiche".
Tim Long

19
+1 per riaprire. Come molti altri argomenti interessanti "dipende" ed è molto utile analizzare i compromessi quando si prendono decisioni. Il fatto che le persone confondano le opinioni con i fatti nelle risposte non lo nega. Passare al setaccio il fango è un esercizio che dovrebbe essere lasciato al lettore.
Aron,

12
Concordo anche che questa domanda dovrebbe essere riaperta in quanto è correlata alle migliori pratiche. A proposito, le migliori pratiche sono sempre opinioni che possono aiutare gli altri.
Ajay Sharma,

6
Microsoft afferma: "Non restituire codici di errore. Le eccezioni sono il mezzo principale per segnalare errori nei framework". e "... Se un membro non può fare con successo ciò per cui è stato progettato, dovrebbe essere considerato un errore di esecuzione e deve essere generata un'eccezione.". msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75

4
Queste potrebbero essere un'eccezione totalmente sensata, dipende solo da quali metodi le lancia. Un metodo chiamato IsCredentialsValid(username,password)non deve generare un'eccezione se il nome utente o la password non sono validi, ma restituiscono false. Ma dire che un metodo che legge i dati dal database potrebbe legittimamente gettare tale eccezione, se l'autenticazione fallisce. In breve: dovresti generare un'eccezione se un metodo non è in grado di eseguire l'attività che dovrebbe svolgere.
Jacques B

Risposte:


629

La mia linea guida personale è: viene generata un'eccezione quando un'assunzione fondamentale dell'attuale blocco di codice viene trovata falsa.

Esempio 1: dire che ho una funzione che dovrebbe esaminare una classe arbitraria e restituire vero se quella classe eredita da List <>. Questa funzione pone la domanda "Questo oggetto è un discendente di Elenco?" Questa funzione non dovrebbe mai generare un'eccezione, perché non ci sono aree grigie nel suo funzionamento - ogni singola classe o eredita o non eredita dall'Elenco <>, quindi la risposta è sempre "sì" o "no".

Esempio 2: dire che ho un'altra funzione che esamina un Elenco <> e restituisce vero se la sua lunghezza è maggiore di 50 e falso se la lunghezza è inferiore. Questa funzione pone la domanda "Questo elenco contiene più di 50 elementi?" Ma questa domanda fa un presupposto: presuppone che l'oggetto che gli viene dato sia un elenco. Se lo considero un NULL, tale presupposto è falso. In tal caso, se la funzione restituisce sia vero o falso, allora è rompere le proprie regole. La funzione non può restituire nulla e dichiarare di aver risposto correttamente alla domanda. Quindi non ritorna - genera un'eccezione.

Questo è paragonabile all'errore logico "domanda caricata" . Ogni funzione pone una domanda. Se l'input che viene fornito rende tale domanda un errore, quindi genera un'eccezione. Questa linea è più difficile da tracciare con funzioni che restituiscono nulla, ma la linea di fondo è: se vengono violati i presupposti della funzione sui suoi input, dovrebbe generare un'eccezione invece di tornare normalmente.

L'altro lato di questa equazione è: se trovi che le tue funzioni generano eccezioni frequentemente, allora probabilmente devi affinare le loro ipotesi.


15
Esattamente! Viene generata un'eccezione quando e solo quando le precondizioni di funzione (ipotesi sugli argomenti) vengono infrante !
Lightman,

11
In linguistica questo è talvolta chiamato fallimento presupposto . L'esempio classico è dovuto a Bertrand Russell: "Il re di Francia è calvo" non si può rispondere si o no, (resp. "Il re di Francia è calvo" non è né vero né falso), perché contiene un presupposto falso , cioè che esiste un re di Francia. L'errore di presupposizione è spesso visto con descrizioni definite, ed è comune durante la programmazione. Ad esempio "La testa di una lista" ha un presupposto fallito quando una lista è vuota, quindi è opportuno lanciare un'eccezione.
Mohan,

Questa è forse la migliore spiegazione!
gaurav,

Grazie. Così. Tanto. "Il re di Francia è calvo". L'ho già sentito prima quando cercavo la giungla di Meinong .... :) Grazie. @Mohan
ErlichBachman,

285

Perché sono cose che accadranno normalmente. Le eccezioni non sono i meccanismi di flusso di controllo. Gli utenti spesso sbagliano le password, non è un caso eccezionale. Le eccezioni dovrebbero essere una cosa davvero rara, UserHasDiedAtKeyboardtipo le situazioni.


3
Hmm, no. Le eccezioni possono essere utilizzate come meccanismi di flusso di controllo se non sono richieste le massime prestazioni, il che è vero per la maggior parte delle app Web. Python utilizza l'eccezione 'StopIteration' per terminare gli iteratori e funziona molto bene. Il costo è nulla rispetto a IO, ecc.
Seun Osewa

9
+1 risposta eccellente. Sono così frustrato dagli sviluppatori che lavorano sulle API che devo consumare e generare Eccezioni per ogni piccola cosa. MOLTO pochi casi richiedono davvero delle eccezioni. Se hai definito 25 diversi tipi di eccezioni, dai un'occhiata al tuo design, potresti sbagliare.
settimane

1
dovrebbe esserci un'eccezione quando l'utente tenta un'azione illegale a cui non è consentito manipolando il codice della pagina Web, ad esempio eliminando i post di qualcun altro qui su StackOverflow?
Rajat Gupta,

30
Eccezioni SONO i meccanismi del flusso di controllo. Puoi lanciarli. Puoi prenderli. Hai spostato il controllo su un altro codice. Questo è il flusso di controllo. L'unico motivo per cui le lingue hanno delle eccezioni è che puoi scrivere un codice semplice senza chiedere "quella cosa è fallita?" dopo tutto quello che fai. Haskell, ad esempio, non ha eccezioni perché le monadi e la notazione possono automatizzare il controllo degli errori.
Jesse,

2
Le eccezioni sono più che meccanismi di flusso di controllo. Forniscono al cliente (metodo) informazioni utili su risultati eccezionali di cui devono essere a conoscenza e gestite. Cioè, usate correttamente, le eccezioni rendono le API più solide
idelvall

67

Le mie piccole linee guida sono fortemente influenzate dal grande libro "Codice completo":

  • Utilizzare le eccezioni per notificare cose che non devono essere ignorate.
  • Non utilizzare eccezioni se l'errore può essere gestito localmente
  • Assicurati che le eccezioni siano allo stesso livello di astrazione del resto della tua routine.
  • Le eccezioni dovrebbero essere riservate a ciò che è veramente eccezionale .

35

NON è un'eccezione se il nome utente non è valido o la password non è corretta. Queste sono cose che dovresti aspettarti nel normale flusso operativo. Le eccezioni sono cose che non fanno parte del normale funzionamento del programma e sono piuttosto rare.

EDIT: Non mi piace usare le eccezioni perché non si può dire se un metodo genera un'eccezione semplicemente guardando la chiamata. Ecco perché le eccezioni dovrebbero essere utilizzate solo se non riesci a gestire la situazione in modo decente (pensa "memoria insufficiente" o "il computer è in fiamme").


"Non mi piace usare le eccezioni perché non si può dire se un metodo genera un'eccezione solo guardando la chiamata." Ecco perché ci sono eccezioni controllate per le lingue che le supportano.
Newtopian,

1
Le eccezioni verificate presentano una serie di problemi. Preferirei comunque usare le eccezioni di "circostanze eccezionali", non per cose che fanno parte del normale flusso di lavoro.
EricSchaefer,

2
In risposta alla tua modifica. Ho sempre inserito nei miei documenti xml alla fine della sezione di riepilogo le eccezioni che la funzione genera in modo da poter vedere quelle informazioni in intellisense.
Matthew Vines,

1
dovrebbe esserci un'eccezione quando l'utente tenta un'azione illegale a cui non è consentito manipolando il codice della pagina Web, ad esempio eliminando i post di qualcun altro qui su StackOverflow?
Rajat Gupta,

1
Sembra che tu stia parlando degli errori di gestione (della nostra memoria, del computer in fiamme) rispetto alle eccezioni del codice di gestione (record mancante, tipo di input non valido, ecc.). Penso che ci sia una netta differenza tra i due.
Chuck Burgess,

28

Una regola empirica è usare le eccezioni nel caso di qualcosa che normalmente non si poteva prevedere. Esempi sono la connettività al database, il file mancante sul disco, ecc. Per gli scenari che è possibile prevedere, vale a dire gli utenti che tentano di accedere con una password errata, è necessario utilizzare funzioni che restituiscono valori booleani e che sanno come gestire la situazione con grazia. Non si desidera interrompere bruscamente l'esecuzione lanciando un'eccezione solo perché qualcuno ha sbagliato a digitare la propria password.


6
non è necessario interrompere l'esecuzione del programma su un'eccezione ... generare l'eccezione, il chiamante rileva l'eccezione e deve gestirla, se possibile, registrare ed errori e andare avanti. È una "cattiva forma" continuare a lanciare eccezioni nello stack di chiamate: prenderlo dove si verifica e
gestirlo

2
ma perché anche lanciarli se riesci a gestirlo direttamente. se la password è errata o qualcosa non va, lascio semplicemente un if restituire un falso e dare un errore
My1

" file mancante su disco " La maggior parte dei framework linguistici, ad esempio .NET framework, fornisce anche API per verificare l'esistenza dei file. Perché non usarli prima di accedere direttamente al file!
user1451111

23

Altri propongono che le eccezioni non debbano essere utilizzate perché l'account di accesso errato deve essere previsto in un flusso normale se l'utente digita male. Non sono d'accordo e non capisco il ragionamento. Confrontalo con l'apertura di un file .. se il file non esiste o non è disponibile per qualche motivo, verrà generata un'eccezione dal framework. L'uso della logica sopra riportata è stato un errore di Microsoft. Avrebbero dovuto restituire un codice di errore. Lo stesso vale per analisi, richieste web, ecc. Ecc.

Non considero un cattivo accesso come parte di un flusso normale, è eccezionale. Normalmente l'utente digita la password corretta e il file esiste. I casi eccezionali sono eccezionali ed è perfettamente bene usare eccezioni per quelli. Complicare il codice propagando i valori di ritorno attraverso n livelli in alto nello stack è uno spreco di energia e si tradurrà in codice disordinato. Fai la cosa più semplice che potrebbe funzionare. Non ottimizzare prematuramente usando codici di errore, raramente succedono cose eccezionali per definizione e le eccezioni non costano nulla a meno che non le lanci.


Tranne che puoi verificare che il file esista prima di chiamare open (a seconda del tuo framework ovviamente). Quindi esiste quella funzione e quindi se il file svanisce tra il controllo e si sta tentando di aprirlo, questa è un'eccezione.
Blowdart,

7
Il file esistente non significa che l'utente è autorizzato a scrivere nel file, ad esempio. Controllare ogni possibile problema è davvero noioso e soggetto a errori. + stai duplicando il codice (DRY).
Bjorn Reppen,

Un punto con la password non valida L'eccezione è che qualsiasi lentezza rispetto alla soluzione del codice di ritorno non sarà percepibile per l'utente umano che digita una password.
paperhorse,

7
"Complicare il codice propagando i valori di ritorno attraverso n livelli in alto nello stack è uno spreco di energia e si tradurrà in codice disordinato". Questo, per me, è una ragione molto forte per usare le eccezioni. Il buon codice è generalmente composto da piccole funzioni. Non si desidera passare più volte quel codice di errore da una piccola funzione all'altra.
Beluchin,

Penso che la confusione derivi da un presupposto forse che un risultato prevedibile di un loginmetodo di tipo sarebbe che una password potrebbe essere errata, potrebbe essere effettivamente utilizzata per determinarlo e non vorrebbe avere un'eccezione in questo caso; mentre in uno file openscenario di tipo, che ha un risultato particolare desiderato, se il sistema non è in grado di fornire il risultato a causa di parametri di input errati o di un fattore esterno, questo è un uso perfettamente logico di un'eccezione.
theMayer

17

Le eccezioni sono un effetto un po 'costoso, ad esempio se si dispone di un utente che fornisce una password non valida, in genere è una buona idea restituire un flag di errore o qualche altro indicatore che non è valido.

Ciò è dovuto al modo in cui vengono gestite le eccezioni, i veri input errati e gli elementi di arresto critici univoci dovrebbero essere eccezioni, ma non informazioni di accesso non riuscite.


14

Penso che dovresti lanciare un'eccezione solo quando non c'è niente che puoi fare per uscire dal tuo stato attuale. Ad esempio, se si sta allocando memoria e non c'è nessuno da allocare. Nei casi indicati, è possibile ripristinare chiaramente da tali stati e restituire di conseguenza un codice di errore al chiamante.


Vedrai molti consigli, anche nelle risposte a questa domanda, che dovresti gettare eccezioni solo in circostanze "eccezionali". Questo sembra superficialmente ragionevole, ma è un consiglio imperfetto, perché sostituisce una domanda ("quando dovrei lanciare un'eccezione") con un'altra domanda soggettiva ("ciò che è eccezionale"). Segui invece il consiglio di Herb Sutter (per C ++, disponibile nell'articolo Dr Dobbs When and How to Use Exceptions , e anche nel suo libro con Andrei Alexandrescu, C ++ Coding Standards ): lancia un'eccezione if, e solo se

  • non è soddisfatta una condizione preliminare (che in genere rende impossibile una delle seguenti) o
  • l'alternativa non riuscirebbe a soddisfare una post-condizione o
  • l'alternativa non riuscirebbe a mantenere un invariante.

Perché è meglio? Non sostituisce la domanda con diverse domande su precondizioni, postcondizioni e invarianti? Questo è meglio per diversi motivi connessi.

  • Presupposti, postcondizioni e invarianti sono le caratteristiche progettuali del nostro programma (la sua API interna), mentre la decisione throwè un dettaglio di implementazione. Ci costringe a tenere a mente che dobbiamo considerare la progettazione e la sua implementazione separatamente, e il nostro lavoro durante l'implementazione di un metodo è quello di produrre qualcosa che soddisfi i vincoli di progettazione.
  • Ci costringe a pensare in termini di precondizioni, postcondizioni e invarianti, che sono le uniche ipotesi che i chiamanti del nostro metodo dovrebbero fare, e sono espresse con precisione, consentendo un libero accoppiamento tra le componenti del nostro programma.
  • Questo accoppiamento lento ci consente quindi di riformattare l'implementazione, se necessario.
  • Le post-condizioni e gli invarianti sono verificabili; risulta in un codice che può essere facilmente testato in unità, poiché le condizioni post-predicate sono predicate dal nostro codice di test unitario (asserzione).
  • Pensare in termini di post-condizioni produce naturalmente un design che ha successo come post-condizione , che è lo stile naturale per usare le eccezioni. Il normale ("felice") percorso di esecuzione del programma è strutturato in modo lineare, con tutto il codice di gestione degli errori spostato nelle catchclausole.

10

Direi che non ci sono regole rigide e veloci su quando usare le eccezioni. Tuttavia ci sono buoni motivi per usarli o non usarli:

Motivi per utilizzare le eccezioni:

  • Il flusso di codice per il caso comune è più chiaro
  • Può restituire informazioni complesse sull'errore come oggetto (sebbene ciò possa essere ottenuto anche usando il parametro "out" dell'errore passato per riferimento)
  • Le lingue generalmente forniscono alcune funzionalità per gestire la pulizia ordinata in caso di eccezione (provare / finalmente in Java, usando in C #, RAII in C ++)
  • Nel caso in cui non venga generata alcuna eccezione, l'esecuzione a volte può essere più rapida della verifica dei codici di ritorno
  • In Java, le eccezioni controllate devono essere dichiarate o rilevate (anche se questo può essere un motivo contrario)

Motivi per non utilizzare le eccezioni:

  • A volte è eccessivo se la gestione degli errori è semplice
  • Se le eccezioni non sono documentate o dichiarate, potrebbero non essere rilevate chiamando il codice, il che potrebbe essere peggio che se il codice chiamante ignorasse semplicemente un codice di ritorno (uscita dell'applicazione vs errore silenzioso - che è peggio può dipendere dallo scenario)
  • In C ++, il codice che utilizza le eccezioni deve essere sicuro dalle eccezioni (anche se non le lanci o le prendi, ma invii indirettamente una funzione di lancio)
  • In C ++, è difficile dire quando una funzione potrebbe essere lanciata, quindi devi essere paranoico sulla sicurezza delle eccezioni se le usi
  • Generare eccezioni è generalmente molto più costoso rispetto al controllo di una bandiera di ritorno

In generale, sarei più propenso a utilizzare le eccezioni in Java che in C ++ o C #, perché sono dell'opinione che un'eccezione, dichiarata o meno, sia fondamentalmente parte dell'interfaccia formale di una funzione, poiché la modifica della garanzia delle eccezioni può rompere il codice chiamante. Il più grande vantaggio di usarli in Java IMO, è che sai che il tuo chiamante DEVE gestire l'eccezione e questo migliora le possibilità di comportamento corretto.

Per questo motivo, in qualsiasi lingua, deriverei sempre tutte le eccezioni in un livello di codice o API da una classe comune, in modo che il codice chiamante possa sempre garantire la cattura di tutte le eccezioni. Inoltre, considererei male generare classi di eccezioni specifiche dell'implementazione, quando si scrive un'API o una libreria (ovvero racchiudere le eccezioni dai livelli inferiori in modo che l'eccezione ricevuta dal chiamante sia comprensibile nel contesto dell'interfaccia).

Si noti che Java fa la distinzione tra eccezioni generali e di runtime in quanto quest'ultima non deve essere dichiarata. Vorrei utilizzare le classi di eccezioni di Runtime solo quando sai che l'errore è il risultato di un bug nel programma.


5

Le classi di eccezione sono come le classi "normali". Si crea una nuova classe quando "è" un diverso tipo di oggetto, con campi e operazioni diverse.

Come regola generale, dovresti provare l'equilibrio tra il numero di eccezioni e la granularità delle eccezioni. Se il tuo metodo genera più di 4-5 diverse eccezioni, puoi probabilmente fonderne alcune in eccezioni più "generali" (ad esempio nel caso "AuthenticationFailedException") e utilizzare il messaggio di eccezione per dettagliare ciò che è andato storto. A meno che il codice non li gestisca in modo diverso, non è necessario creare molte classi di eccezioni. E se lo fa, potresti semplicemente restituire un enum con l'errore che si è verificato. È un po 'più pulito in questo modo.


5

Se è il codice in esecuzione all'interno di un ciclo che probabilmente causerà ripetutamente un'eccezione, quindi lanciare eccezioni non è una buona cosa, perché sono piuttosto lenti per il grande N. Ma non c'è nulla di sbagliato nel lanciare eccezioni personalizzate se le prestazioni non lo sono un problema. Assicurati solo di avere un'eccezione di base che tutti ereditano, chiamata BaseException o qualcosa del genere. BaseException eredita System.Exception, ma tutte le eccezioni ereditano BaseException. Puoi anche avere un albero di tipi di eccezioni per raggruppare tipi simili, ma questo può o meno essere eccessivo.

Quindi, la risposta breve è che se non causa una penalità di prestazione significativa (cosa che non dovrebbe fare a meno che non si stiano dando molte eccezioni), quindi andare avanti.


Mi è piaciuto molto il tuo commento sulle eccezioni all'interno di un ciclo e ho pensato di provarlo da solo. Ho scritto un programma di esempio eseguendo un tempo di ciclo int.MaxValuee generando un'eccezione "divide per zero" in esso. La versione IF / ELSE, in cui stavo verificando se il dividendo non era zero prima dell'operazione di divisione , è stata completata in 6082 ms e 15407722 tick mentre la versione TRY / CATCH, in cui stavo generando l'eccezione e rilevando l'eccezione , è stata completata nel 28174385 tick e 71371326155 tick: un enorme 4632 volte più della versione if / else.
user1451111

3

Sono d'accordo con japollock lassù: lanciare un'accettazione quando non si è sicuri del risultato di un'operazione. Chiamate alle API, accesso ai filesystem, chiamate al database, ecc. Ogni volta che si oltrepassa i "confini" dei linguaggi di programmazione.

Vorrei aggiungere, sentiti libero di lanciare un'eccezione standard. A meno che tu non abbia intenzione di fare qualcosa di "diverso" (ignora, invia per e-mail, registra, mostra che l'immagine della balena di Twitter è così ecc.), Quindi non preoccuparti delle eccezioni personalizzate.


3

la regola empirica per il lancio di eccezioni è piuttosto semplice. lo fai quando il tuo codice è entrato in uno stato NON VALIDABILE INVALIDO. se i dati sono compromessi o non è possibile ripristinare l'elaborazione che si è verificata fino al punto, è necessario terminarli. davvero cos'altro puoi fare? la tua logica di elaborazione alla fine fallirà altrove. se riesci a recuperare in qualche modo, fallo e non gettare eccezioni.

nel tuo caso particolare, se sei stato costretto a fare qualcosa di stupido come accettare il prelievo di denaro e solo allora controlla l'utente / password devi terminare il processo lanciando un'eccezione per notificare che è successo qualcosa di brutto e prevenire ulteriori danni.


2

In generale, si desidera generare un'eccezione per tutto ciò che può accadere nella propria applicazione "Eccezionale"

Nel tuo esempio, entrambe queste eccezioni sembrano chiamarle tramite una convalida password / nome utente. In tal caso si può affermare che non è davvero eccezionale che qualcuno digiti male un nome utente / password.

Sono "eccezioni" al flusso principale del tuo UML ma sono più "rami" nell'elaborazione.

Se hai tentato di accedere al tuo file o database passwd e non ci sei riuscito, sarebbe un caso eccezionale e meriterebbe di generare un'eccezione.


" Se tentassi di accedere al tuo file o database passwd e non ci riuscissi, questo sarebbe un caso eccezionale e giustificherebbe il lancio di un'eccezione. " La maggior parte dei framework linguistici, ad esempio .NET framework, fornisce anche API per verificare l'esistenza dei file. Perché non usarli prima di accedere direttamente al file!
user1451111

2

In primo luogo, se gli utenti dell'API non sono interessati a errori specifici e precisi, avere eccezioni specifiche per loro non ha alcun valore.

Dato che spesso non è possibile sapere cosa può essere utile per i tuoi utenti, un approccio migliore è avere le eccezioni specifiche, ma assicurarsi che ereditino da una classe comune (ad esempio, std :: exception o i suoi derivati ​​in C ++). Ciò consente al cliente di rilevare eccezioni specifiche se lo desidera, o l'eccezione più generale se non gli interessa.


2

Le eccezioni sono intese per eventi che sono comportamenti anomali, errori, guasti e simili. Il comportamento funzionale, l'errore dell'utente, ecc., Dovrebbe essere gestito invece dalla logica del programma. Poiché un account o una password non validi sono parte prevista del flusso logico in una routine di accesso, dovrebbe essere in grado di gestire tali situazioni senza eccezioni.


2

Ho problemi filosofici con l'uso delle eccezioni. Fondamentalmente, ti aspetti che si verifichi uno scenario specifico, ma invece di gestirlo esplicitamente stai spingendo il problema fuori per essere gestito "altrove". E dove si trova "altrove" può essere la supposizione di chiunque.


2

Direi che generalmente ogni fondamentalismo porta all'inferno.

Certamente non vorrai finire con un flusso guidato dalle eccezioni, ma evitare del tutto le eccezioni è anche una cattiva idea. Devi trovare un equilibrio tra entrambi gli approcci. Quello che non vorrei fare è creare un tipo di eccezione per ogni situazione eccezionale. Questo non è produttivo.

Quello che generalmente preferisco è creare due tipi base di eccezioni che vengono utilizzate in tutto il sistema: LogicalException e TechnicalException . Questi possono essere ulteriormente distinti dai sottotipi, se necessario, ma generalmente non è necessario.

L'eccezione tecnica indica l'eccezione davvero inattesa come il server di database inattivo, la connessione al servizio Web ha generato IOException e così via.

D'altra parte, le eccezioni logiche vengono utilizzate per propagare la situazione errata meno grave agli strati superiori (generalmente un risultato di convalida).

Si noti che anche l'eccezione logica non deve essere utilizzata su base regolare per controllare il flusso del programma, ma piuttosto per evidenziare la situazione in cui il flusso dovrebbe realmente terminare. Se utilizzati in Java, entrambi i tipi di eccezione sono RuntimeException sottoclassi di e la gestione degli errori è fortemente orientata all'aspetto.

Quindi nell'esempio di accesso potrebbe essere saggio creare qualcosa come AuthenticationException e distinguere le situazioni concrete da valori enum come UsernameNotExisting , PasswordMismatch ecc. Quindi non si finirà per avere un'enorme gerarchia di eccezioni e è possibile mantenere i blocchi di cattura a livello mantenibile . Puoi anche utilizzare facilmente un meccanismo generico di gestione delle eccezioni poiché le eccezioni sono state classificate e sai abbastanza bene cosa propagare all'utente e come.

Il nostro uso tipico è di lanciare LogicalException durante la chiamata del servizio Web quando l'input dell'utente non è valido. L'eccezione viene assegnata ai dettagli di SOAPFault e quindi nuovamente non inviata all'eccezione sul client, risultando nel mostrare l'errore di convalida in un determinato campo di input della pagina Web poiché l'eccezione ha il mapping corretto a quel campo.

Questa non è certamente l'unica situazione: non è necessario colpire il servizio Web per generare l'eccezione. Sei libero di farlo in qualsiasi situazione eccezionale (come nel caso in cui tu abbia bisogno di fallire velocemente) - è tutto a tua discrezione.


2

per me Eccezione dovrebbe essere lanciata quando una regola tecnica o commerciale richiesta fallisce. ad esempio se un'entità auto è associata a un array di 4 pneumatici ... se uno o più pneumatici sono nulli ... un'eccezione deve essere attivata "NotEnoughTiresException", perché può essere catturata a diversi livelli del sistema e avere un significativo significato attraverso la registrazione. inoltre se proviamo semplicemente a controllare il flusso il valore nullo e ad impedire l'installazione dell'auto. potremmo non trovare mai la fonte del problema, perché la gomma non dovrebbe essere nulla in primo luogo.


1

la ragione principale per evitare di lanciare un'eccezione è che ci sono molte spese generali legate al lancio di un'eccezione.

Una cosa che l'articolo in basso afferma è che un'eccezione è per condizioni ed errori eccezionali.

Un nome utente errato non è necessariamente un errore del programma ma un errore dell'utente ...

Ecco un discreto punto di partenza per le eccezioni in .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx


1

Il lancio di eccezioni provoca lo scioglimento dello stack, il che ha alcuni impatti sulle prestazioni (ammesso, i moderni ambienti gestiti sono migliorati su questo). Sarebbe comunque una cattiva idea lanciare e catturare ripetutamente eccezioni in una situazione nidificata.

Probabilmente più importante di così, le eccezioni sono pensate per condizioni eccezionali. Non dovrebbero essere utilizzati per il normale flusso di controllo, poiché ciò danneggerebbe la leggibilità del codice.


1

Ho tre tipi di condizioni che prendo.

  1. Un input errato o mancante non dovrebbe essere un'eccezione. Utilizzare js lato client e regex lato server per rilevare, impostare gli attributi e inoltrarli alla stessa pagina con i messaggi.

  2. AppException. In genere si tratta di un'eccezione rilevata e gettata nel codice. In altre parole, questi sono quelli che ti aspetti (il file non esiste). Registralo, imposta il messaggio e inoltra nuovamente alla pagina di errore generale. Questa pagina di solito contiene alcune informazioni sull'accaduto.

  3. L'eccezione inaspettata. Questi sono quelli che non conosci. Accedi con i dettagli e inoltrali a una pagina di errore generale.

Spero che sia di aiuto


1

La sicurezza si confonde con il tuo esempio: non devi dire a un utente malintenzionato che esiste un nome utente, ma la password è errata. Sono informazioni extra che non è necessario condividere. Basta dire "il nome utente o la password non sono corretti".


1

La semplice risposta è ogni volta che un'operazione è impossibile (a causa di una delle due applicazioni o perché violerebbe la logica aziendale). Se viene invocato un metodo ed è impossibile fare ciò per cui è stato scritto il metodo, genera un'eccezione. Un buon esempio è che i costruttori generano sempre ArgumentExceptions se non è possibile creare un'istanza utilizzando i parametri forniti. Un altro esempio è InvalidOperationException, che viene generato quando un'operazione non può essere eseguita a causa dello stato di un altro membro o membri della classe.

Nel tuo caso, se viene richiamato un metodo come Login (nome utente, password), se il nome utente non è valido, è effettivamente corretto lanciare un UserNameNotValidException o PasswordNotCorrectException se la password non è corretta. L'utente non può accedere utilizzando i parametri forniti (ovvero è impossibile perché violerebbe l'autenticazione), quindi genera un'eccezione. Anche se potrei avere le tue due eccezioni ereditate da ArgumentException.

Detto questo, se si desidera NON generare un'eccezione perché un errore di accesso può essere molto comune, una strategia è invece quella di creare un metodo che restituisca tipi che rappresentano diversi errori. Ecco un esempio:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

Alla maggior parte degli sviluppatori viene insegnato a evitare le eccezioni a causa del sovraccarico causato dal loro lancio. È fantastico essere attenti alle risorse, ma di solito non a scapito della progettazione dell'applicazione. Questo è probabilmente il motivo per cui ti è stato detto di non gettare le tue due eccezioni. Se utilizzare le eccezioni o meno di solito si riduce alla frequenza con cui si verificherà l'eccezione. Se si tratta di un risultato abbastanza comune o abbastanza prevedibile, in questo caso la maggior parte degli sviluppatori eviterà le eccezioni e creerà invece un altro metodo per indicare il fallimento, a causa del presunto consumo di risorse.

Ecco un esempio di come evitare l'utilizzo di Eccezioni in uno scenario come appena descritto, usando il modello Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

1

A mio avviso, la domanda fondamentale dovrebbe essere se ci si aspetterebbe che il chiamante vorrebbe continuare il normale flusso di programma se si verifica una condizione. Se non lo sai, hai metodi doSomething e trySomething separati, dove il primo restituisce un errore e il secondo no, oppure hai una routine che accetta un parametro per indicare se un'eccezione debba essere lanciata se fallisce). Prendi in considerazione una classe per inviare comandi a un sistema remoto e segnalare le risposte. Alcuni comandi (ad es. Riavvio) causeranno l'invio da parte del sistema remoto di una risposta, ma per un certo periodo di tempo non risponderanno. È quindi utile essere in grado di inviare un comando "ping" e scoprire se il sistema remoto risponde in un periodo di tempo ragionevole senza dover generare un'eccezione se non lo fa t (il chiamante probabilmente si aspetterebbe che i primi tentativi di "ping" fallissero, ma uno alla fine funzionerebbe). D'altra parte, se uno ha una sequenza di comandi come:

  exchange_command ("open tempfile");
  exchange_command ("scrivi dati tempfile {qualunque}");
  exchange_command ("scrivi dati tempfile {qualunque}");
  exchange_command ("scrivi dati tempfile {qualunque}");
  exchange_command ("scrivi dati tempfile {qualunque}");
  exchange_command ("close tempfile");
  exchange_command ("copia tempfile in realfile");

si vorrebbe il fallimento di qualsiasi operazione per interrompere l'intera sequenza. Sebbene sia possibile controllare ciascuna operazione per assicurarsi che abbia esito positivo, è più utile che la routine exchange_command () generi un'eccezione se un comando non riesce.

In realtà, nello scenario sopra riportato può essere utile disporre di un parametro per selezionare una serie di modalità di gestione degli errori: non generare mai eccezioni, generare eccezioni solo per errori di comunicazione o generare eccezioni in tutti i casi in cui un comando non restituisce un "esito positivo "indicazione.


1

"PasswordNotCorrectException" non è un buon esempio per l'utilizzo delle eccezioni. Gli utenti che hanno sbagliato la password devono aspettarsi, quindi non è certo un'eccezione IMHO. Probabilmente ti recuperi anche da esso, mostrando un bel messaggio di errore, quindi è solo un controllo di validità.

Le eccezioni non gestite alla fine interromperanno l'esecuzione, il che è positivo. Se stai restituendo codici falsi, nulli o di errore, dovrai gestire lo stato del programma da solo. Se dimentichi di controllare le condizioni da qualche parte, il tuo programma potrebbe continuare a funzionare con dati errati e potresti avere difficoltà a capire cosa è successo e dove .

Certo, potresti causare lo stesso problema con dichiarazioni di cattura vuote, ma almeno individuarle è più facile e non richiede di capire la logica.

Quindi, come regola generale:

Usali dove non vuoi o semplicemente non riesci a recuperare da un errore.


0

È possibile utilizzare alcune eccezioni generiche per tali condizioni. Ad esempio ArgumentException deve essere utilizzato quando qualcosa va storto con i parametri di un metodo (ad eccezione di ArgumentNullException). Generalmente non avresti bisogno di eccezioni come LessThanZeroException, NotPrimeNumberException ecc. Pensa all'utente del tuo metodo. Il numero delle condizioni che vorrà gestire in modo specifico è uguale al numero del tipo di eccezioni che il metodo deve generare. In questo modo, puoi determinare quante eccezioni dettagliate avrai.

A proposito, cerca sempre di fornire alcuni modi per gli utenti delle tue librerie per evitare eccezioni. TryParse è un buon esempio, esiste in modo da non dover usare int.Parse e catturare un'eccezione. Nel tuo caso, potresti voler fornire alcuni metodi per verificare se il nome utente è valido o la password è corretta, quindi i tuoi utenti (o tu) non dovranno fare molta gestione delle eccezioni. Si spera che ciò comporti un codice più leggibile e migliori prestazioni.


0

In definitiva, la decisione viene presa se è più utile gestire errori a livello di applicazione come questo utilizzando la gestione delle eccezioni o tramite il proprio meccanismo di rotazione a casa come la restituzione di codici di stato. Non penso che ci sia una regola rigida su quale sia meglio, ma prenderei in considerazione:

  • Chi sta chiamando il tuo codice? È un'API pubblica di qualche tipo o una libreria interna?
  • Che lingua stai usando? Se è Java, ad esempio, lanciare un'eccezione (selezionata) comporta un onere esplicito per il chiamante per gestire in qualche modo questa condizione di errore, a differenza di uno stato di ritorno che potrebbe essere ignorato. Potrebbe essere buono o cattivo.
  • Come vengono gestite le altre condizioni di errore nella stessa applicazione? I chiamanti non vorranno avere a che fare con un modulo che gestisce gli errori in modo idiosincratico diversamente da qualsiasi altra cosa nel sistema.
  • Quante cose possono andare storte nella routine in questione e come sarebbero gestite diversamente? Considera la differenza tra una serie di blocchi catch che gestiscono diversi errori e un interruttore su un codice di errore.
  • Hai informazioni strutturate sull'errore che devi restituire? Lanciare un'eccezione ti dà un posto migliore per mettere queste informazioni piuttosto che restituire uno stato.

0

Esistono due principali classi di eccezione:

1) Eccezione del sistema (ad es. Connessione al database persa) o 2) Eccezione dell'utente. (ad es. convalida dell'immissione dell'utente, "password errata")

Ho trovato utile creare la mia classe di eccezione utente e quando voglio generare un errore utente voglio essere gestito in modo diverso (ovvero errore di risorsa visualizzato all'utente), tutto ciò che devo fare nel mio gestore errori principale è controllare il tipo di oggetto :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

0

Alcune cose utili a cui pensare quando si decide se un'eccezione è appropriata:

  1. quale livello di codice si desidera eseguire dopo che si è verificato il candidato dell'eccezione, ovvero quanti livelli dello stack di chiamate devono svolgersi. In genere si desidera gestire un'eccezione il più vicino possibile al luogo in cui si verifica. Per la convalida di nome utente / password, normalmente si gestiscono gli errori nello stesso blocco di codice, piuttosto che lasciare una bolla in sospeso. Quindi un'eccezione non è probabilmente appropriata. (OTOH, dopo tre tentativi di accesso falliti, il flusso di controllo potrebbe spostarsi altrove e qui potrebbe essere appropriata un'eccezione.)

  2. Questo evento è qualcosa che vorresti vedere in un registro degli errori? Non tutte le eccezioni vengono scritte in un registro degli errori, ma è utile chiedere se questa voce in un registro degli errori sarebbe utile, ad esempio, si proverebbe a fare qualcosa al riguardo o sarebbe la spazzatura che si ignora.

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.