it-swarm.it

Quando lanciare un'eccezione?

Ho creato eccezioni per ogni condizione che la mia applicazione non si aspetta. UserNameNotValidException, PasswordNotCorrectException ecc.

Tuttavia mi è stato detto che non dovrei creare eccezioni per quelle 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?

402
Kwan Cheng

La mia linea guida personale è: un'eccezione viene lanciata quando un'assunzione fondamentale del blocco di codice corrente risulta falsa.

Esempio 1: dire che ho una funzione che dovrebbe esaminare una classe arbitraria e restituire true se tale classe eredita dalla lista <>. Questa funzione pone la domanda: "Questo oggetto è un discendente di List?" Questa funzione non dovrebbe mai generare un'eccezione, perché non ci sono aree grigie nelle sue operazioni - ogni singola classe o non eredita da Lista <>, quindi la risposta è sempre "sì" o "no".

Esempio 2: dire che ho un'altra funzione che esamina un elenco <> e restituisce true se la sua lunghezza è maggiore di 50 e false se la lunghezza è minore. Questa funzione pone la domanda: "Questo elenco contiene più di 50 articoli?" Ma questa domanda fa un'ipotesi - presuppone che l'oggetto che gli viene dato sia una lista. Se lo consegno un NULL, allora quell'assunzione è falsa. In tal caso, se la funzione ritorna o vero o falso, allora infrange le sue stesse regole. La funzione non può tornare nulla e afferma di aver risposto correttamente alla domanda. Quindi non ritorna - lancia un'eccezione.

Questo è paragonabile alla "domanda caricata" errore logico. Ogni funzione fa una domanda. Se l'input che viene fornito rende quella domanda un errore, quindi genera un'eccezione. Questa linea è più difficile da disegnare con funzioni che restituiscono nulla, ma la linea di fondo è: se le ipotesi della funzione sui suoi input sono violate, dovrebbe generare un'eccezione invece di ritornare normalmente.

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

577

Perché sono cose che accadranno normalmente. Le eccezioni non sono meccanismi di controllo del flusso. Gli utenti spesso ottengono password errate, non è un caso eccezionale. Le eccezioni dovrebbero essere una cosa veramente rara, le situazioni di tipo UserHasDiedAtKeyboard.

279
blowdart

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

  • Usa le eccezioni per notificare cose che non dovrebbero 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 per ciò che è veramente eccezionale.
61
Commander Keen

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ò sapere se un metodo genera un'eccezione semplicemente osservando la chiamata. Ecco perché le eccezioni dovrebbero essere utilizzate solo se non si è in grado di gestire la situazione in maniera decente (pensare "memoria esaurita" o "il computer è in fiamme").

34
EricSchaefer

Una regola empirica consiste nell'usare eccezioni nel caso di qualcosa che normalmente non si poteva prevedere. Esempi sono la connettività del database, il file mancante sul disco, ecc. Per gli scenari che è possibile prevedere, ovvero gli utenti che tentano di accedere con una password errata, si dovrebbero usare funzioni che restituiscono booleani e sanno come gestire la situazione con garbo. Non si desidera interrompere bruscamente l'esecuzione inviando un'eccezione solo perché qualcuno ha digitato in modo errato la propria password.

25
japollock

Altri propongono che non si debbano utilizzare eccezioni perché ci si può aspettare un accesso errato in un flusso normale se l'utente digita erroneamente. 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. Usando la logica sopra questo è stato un errore di Microsoft. Dovrebbero aver restituito un codice di errore. Lo stesso per parsing, webrequests, ecc., Ecc.

Non considero una brutta parte di accesso 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 nello stack è uno spreco di energia e provocherà un codice disordinato. Fai la cosa più semplice che potrebbe funzionare. Non ottimizzarlo prematuramente usando i codici di errore, raramente succede qualcosa per definizione, e le eccezioni non costano nulla a meno che non le elimini.

23
Bjorn Reppen

Le eccezioni sono un effetto un po 'costoso, se ad esempio si dispone di un utente che fornisce una password non valida, è in genere un'idea migliore 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 critici di arresto unici dovrebbero essere eccezioni, ma non le informazioni di accesso non riuscite.

16
Mitchel Sellers

Penso che dovresti solo fare un'eccezione quando non c'è niente che puoi fare per uscire dal tuo stato attuale. Ad esempio se stai allocando memoria e non ce n'è alcuna da allocare. Nei casi citati puoi chiaramente recuperare da tali stati e puoi restituire di conseguenza un codice di errore al tuo chiamante.


Vedrai un sacco di consigli, incluse le risposte a questa domanda, che dovresti gettare le eccezioni solo in circostanze "eccezionali". Ciò 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 nel articolo Dr Dobbs When e How to Use Exceptions , e anche nel suo libro con Andrei Alexandrescu, C++ Coding Standards ): lancia un'eccezione se, 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 mancherebbe di mantenere invariata.

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

  • Precondizioni, postcondizioni e invarianti sono design caratteristiche del nostro programma (la sua API interna), mentre la decisione di throw è un dettaglio di implementazione. Ci costringe a tenere presente che dobbiamo considerare separatamente il design e la sua implementazione, e il nostro lavoro mentre implementiamo un metodo è produrre qualcosa che soddisfi i vincoli di progettazione.
  • Ci costringe a pensare in termini di precondizioni, postcondizioni e invarianti, che sono le solo ipotesi che i chiamanti del nostro metodo dovrebbero fare, e sono espresse con precisione, consentendo un accoppiamento lento tra i componenti del nostro programma.
  • Questo accoppiamento lento ci consente quindi di ridefinire l'implementazione, se necessario.
  • Le post-condizioni e invarianti sono testabili; risulta in un codice che può essere facilmente testato con l'unità, perché le post-condizioni sono predicate che il nostro codice di test di unità può verificare (asserire).
  • Pensare in termini di post-condizioni produce naturalmente un design che ha il successo come post-condizione , che è lo stile naturale per usare le eccezioni. Il normale ("felice") percorso di esecuzione del tuo programma è presentato linearmente, con tutto il codice di gestione degli errori spostato nelle clausole catch.
14
Jon

Direi che non ci sono regole ferree su quando utilizzare le eccezioni. Tuttavia ci sono buone ragioni per usare o non usarli:

Motivi per utilizzare le eccezioni:

  • Il flusso di codice per il caso comune è più chiaro
  • Può restituire informazioni di errore complesse come un oggetto (anche se questo può essere ottenuto anche utilizzando il parametro error "out" passato per riferimento)
  • Le lingue generalmente forniscono alcune possibilità per la gestione della pulizia ordinata in caso di eccezione (prova/finalmente in Java, usando in C #, RAII in C++)
  • Nel caso in cui non venga lanciata alcuna eccezione, l'esecuzione può a volte essere più veloce di controllare i codici di ritorno
  • In Java, le eccezioni controllate devono essere dichiarate o catturate (sebbene questo possa essere un motivo contro)

Motivi per non usare eccezioni:

  • A volte è eccessivo se la gestione degli errori è semplice
  • Se le eccezioni non sono documentate o dichiarate, potrebbero non essere richiamate chiamando il codice, il che potrebbe essere peggio che se il codice chiamante abbia appena ignorato un codice di ritorno (l'uscita dell'applicazione vs l'errore silenzioso - che è peggio potrebbe dipendere dallo scenario)
  • In C++, il codice che utilizza le eccezioni deve essere eccezionalmente sicuro (anche se non lo si butta o si cattura, ma si chiama indirettamente una funzione di lancio)
  • In C++, è difficile dire quando una funzione può essere lanciata, quindi devi essere paranoico riguardo alla sicurezza delle eccezioni se le usi
  • Generalmente le eccezioni di lancio e cattura sono significativamente più costose rispetto al controllo di un flag di restituzione

In generale, sarei più propenso a utilizzare le eccezioni in Java rispetto a 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 di eccezione potrebbe rompere il codice chiamante. Il più grande vantaggio di utilizzarli in Java IMO, è che tu sai che il tuo chiamante DEVE gestire l'eccezione e questo migliora la possibilità di un comportamento corretto.

Per questo motivo, in qualsiasi linguaggio, 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 di rilevare tutte le eccezioni. Inoltre, considererei errato pubblicare classi di eccezioni specifiche dell'implementazione, quando si scrive un'API o una libreria (ad esempio, avvolgere le eccezioni dai livelli inferiori in modo che l'eccezione ricevuta dal chiamante sia comprensibile nel contesto dell'interfaccia).

Si noti che Java distingue tra eccezioni generali e runtime in quanto non è necessario dichiarare quest'ultimo. Userei solo le classi di eccezioni Runtime quando sai che l'errore è il risultato di un bug nel programma.

10
Robert

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

Come regola generale, dovresti cercare un equilibrio tra il numero di eccezioni e la granularità delle eccezioni. Se il tuo metodo genera più di 4-5 diverse eccezioni, puoi probabilmente unirle ad alcune più eccezioni "generali", (ad esempio nel tuo caso "AuthenticationFailedException"), e usare il messaggio di eccezione per dettagliare cosa è andato storto. A meno che il tuo codice non gestisca ognuno di essi 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
Shachar

Se il codice è in esecuzione in un ciclo che probabilmente causerà un'eccezione più e più volte, quindi lanciare eccezioni non è una buona cosa, perché sono piuttosto lente per il grande N. Ma non c'è niente di sbagliato nel lanciare eccezioni personalizzate se la performance non è un problema. Assicurati di avere un'eccezione di base ereditata da tutti, chiamata BaseException o qualcosa del genere. BaseException eredita System.Exception, ma tutte le eccezioni ereditano BaseException. È anche possibile avere un albero di tipi di eccezioni per raggruppare tipi simili, ma questo può essere o non essere eccessivo.

Quindi, la risposta breve è che se non causa una significativa penalizzazione delle prestazioni (che non dovrebbe a meno che tu non stia gettando molte eccezioni), allora vai avanti.

5
Charles Graham

Sono d'accordo con Japollock, laggiù, quando si è incerti sull'esito di un'operazione. Chiamate alle API, accesso ai filesystem, chiamate al database, ecc. Ogni volta che passi oltre i "confini" dei tuoi linguaggi di programmazione.

Mi piacerebbe aggiungere, sentiti libero di lanciare un'eccezione standard. A meno che non stiate per fare qualcosa di "diverso" (ignorate, inviate via e-mail, registrate, mostrate quello che c'è in balle di Twitter, ecc.), Quindi non preoccupatevi delle eccezioni personalizzate.

3
dclaysmith

la regola generale per lanciare le eccezioni è piuttosto semplice. lo fai quando il tuo codice è entrato in uno stato NON VALIDO NON RISERVATO. se i dati sono compromessi o non è possibile riavviare il processo che si è verificato fino al punto, è necessario chiuderlo. davvero cos'altro puoi fare? la tua logica di elaborazione alla fine fallirà altrove. se puoi recuperare in qualche modo, fallo e non lanciare alcuna eccezione.

nel tuo caso particolare se sei stato costretto a fare qualcosa di sciocco come accettare il prelievo di denaro e solo dopo controlla utente/pasword dovresti interrompere il processo lanciando un'eccezione per notificare che qualcosa di brutto è successo e prevenire ulteriori danni.

3
goran

Direi che in genere ogni fondamentalismo porta all'inferno.

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

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

L'eccezione tecnica denota l'eccezione davvero inaspettata come il server di database inattivo, la connessione al servizio web ha gettato IOException e così via.

D'altra parte le eccezioni logiche sono usate per propagare la situazione erronea meno grave ai livelli superiori (generalmente qualche risultato di validazione).

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

Quindi nell'esempio di accesso potrebbe essere saggio creare qualcosa come AuthenticationException e distinguere le situazioni concrete da valori di enum come sernameNotExisting, PasswordMismatch ecc. Quindi non finirai avere un'enorme gerarchia di eccezioni e può mantenere i blocchi di cattura a livello gestibile. È anche possibile utilizzare facilmente un meccanismo di gestione delle eccezioni generico poiché le eccezioni sono categorizzate e sanno bene cosa propagare all'utente e come.

Il nostro tipico utilizzo consiste nel lanciare LogicalException durante la chiamata al servizio Web quando l'input dell'utente non è valido. L'eccezione viene sottoposta a marshalling nei dettagli SOAPFault e quindi viene ripristinata nuovamente l'eccezione per l'eccezione sul client, che risulta nel mostrare l'errore di convalida su un determinato campo di input della pagina Web poiché l'eccezione ha una mappatura corretta per quel campo.

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

2
Petr Macek

Le eccezioni sono intese per eventi che sono comportamenti anomali, errori, errori e così via. Comportamento funzionale, errore utente, ecc., Dovrebbero invece essere gestiti dalla logica del programma. Poiché un account o una password errati è una parte prevista del flusso logico in una routine di accesso, dovrebbe essere in grado di gestire tali situazioni senza eccezioni.

2
Joe Skora

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

Nel tuo esempio, entrambe le eccezioni sembrano essere chiamate tramite una convalida password/nome utente. In tal caso si può sostenere che non è davvero eccezionale che qualcuno possa digitare male un nome utente/password.

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

Se si è tentato di accedere al proprio file o database passwd e non si è riusciti a farlo, si tratterebbe di un caso eccezionale e si giustificherebbe il lancio di un'eccezione.

2
Gord

Innanzitutto, se gli utenti della tua API non sono interessati a errori specifici ea grana fine, avere eccezioni specifiche per loro non ha alcun valore.

Poiché spesso non è possibile sapere cosa può essere utile ai tuoi utenti, un approccio migliore è quello di avere le eccezioni specifiche, ma assicurarti che ereditino da una classe comune (ad esempio, std :: exception o le sue derivate in C++). Ciò consente al tuo cliente di rilevare eccezioni specifiche se lo desidera, o l'eccezione più generale se non gli interessa.

2
Jason Etheridge

Ho problemi filosofici con l'uso delle eccezioni. Fondamentalmente, ti aspetti uno scenario specifico, ma piuttosto che gestirlo in modo esplicito stai eliminando il problema per essere gestito "altrove". E dove quel "altrove" è può essere la supposizione di chiunque.

2
Dan

Ho tre tipi di condizioni che catturo.

  1. L'input non valido o mancante non dovrebbe essere un'eccezione. Utilizza sia js lato client che regex lato server per rilevare, impostare gli attributi e inoltrare di nuovo alla stessa pagina con i messaggi.

  2. AppException. Solitamente si tratta di un'eccezione che viene rilevata e inserita nel codice. In altre parole, questi sono quelli che ti aspetti (il file non esiste). Registralo, imposta il messaggio e torna alla pagina di errore generale. Questa pagina di solito ha un po 'di informazioni su quello che è successo.

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

Spero che questo ti aiuti

1
Michael

per me L'eccezione dovrebbe essere generata quando una regola tecnica o commerciale richiesta non riesce. ad esempio se un'entità automobilistica è associata a un array di 4 pneumatici ... se uno pneumatico o più sono nulli ... un'eccezione dovrebbe essere "NotEnoughTiresException", perché può essere catturata a diversi livelli del sistema e avere una significativa significato attraverso la registrazione. inoltre se proviamo solo a controllare il flusso del nulla e ad impedire l'instanciazione della macchina. potremmo non trovare mai la fonte del problema, perché in primo luogo il pneumatico non dovrebbe essere nullo.

1
Genjuro

La risposta semplice è, ogni volta che un'operazione è impossibile (a causa di entrambe le applicazioni OR perché violerebbe la logica aziendale). Se viene invocato un metodo ed è impossibile fare ciò che è stato scritto dal metodo, lanciare un'eccezione. Un buon esempio è che i costruttori lanciano 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 invocato un metodo come Login (nome utente, password), se il nome utente non è valido, è effettivamente corretto lanciare un valore UserNameNotValidException o PasswordNotCorrectException se la password non è corretta. L'utente non può accedere con i parametri forniti (cioè è impossibile perché violerebbe l'autenticazione), quindi lanciare 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 consiste invece nel 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 di evitare le eccezioni a causa del sovraccarico causato dal loro lancio. È fantastico essere attenti alle risorse, ma solitamente non a discapito della progettazione dell'applicazione. Questa è probabilmente la ragione per cui ti è stato detto di non lanciare le tue due eccezioni. Se utilizzare Eccezioni o meno, in genere si riduce alla frequenza con cui si verificherà l'eccezione. Se si tratta di un risultato abbastanza comune o abbastanza prevedibile, questo è il momento in cui la maggior parte degli sviluppatori eviterà Eccezioni e creerà invece un altro metodo per indicare il fallimento, a causa del supposto consumo di risorse.

Ecco un esempio di come evitare l'uso di Eccezioni in uno scenario come appena descritto, utilizzando 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
core

A mio avviso, la domanda fondamentale dovrebbe essere se ci si aspetterebbe che il chiamante desideri continuare il normale flusso del programma se si verifica una condizione. Se non lo sai, o hai metodi separati DoSomething e trySomething, dove il primo restituisce un errore e quest'ultimo non lo fa, oppure ha una routine che accetta un parametro per indicare se deve essere generata un'eccezione se fallisce). Considerare una classe per inviare comandi a un sistema remoto e segnalare le risposte. Alcuni comandi (ad es. Riavvio) causano l'invio di una risposta da parte del sistema remoto, ma non rispondono per un certo periodo di tempo. È quindi utile poter inviare un comando "ping" e scoprire se il sistema remoto risponde in un ragionevole lasso di tempo senza dover generare un'eccezione se non lo fa (il chiamante probabilmente si aspetterebbe che i primi pochi " ping "i tentativi fallirebbero, ma uno alla fine funzionerebbe). D'altra parte, se uno ha una sequenza di comandi come:

 exchange_command ("open tempfile"); 
 exchange_command ("scrivi tempfile data {whatever}"); 
 exchange_command ("scrivi tempfile data {whatever}"); 
 exchange_command ("scrivi tempfile data {whatever}"); 
 exchange_command ("scrivi tempfile data {whatever}"); 
 exchange_command ("chiudi tempfile"); 
 exchange_command ("copia tempfile in realfile"); 

uno vorrebbe il fallimento di qualsiasi operazione di abortire l'intera sequenza. Mentre è possibile controllare ciascuna operazione per assicurarsi che abbia avuto successo, è più utile avere la routine exchange_command () per lanciare un'eccezione se un comando fallisce.

In realtà, nello scenario sopra riportato può essere utile avere un parametro per selezionare un numero di modalità di gestione degli errori: non lanciare eccezioni, lanciare eccezioni solo per errori di comunicazione o lanciare eccezioni in tutti i casi in cui un comando non restituisce un "successo" "indicazione.

1
supercat

il motivo principale per evitare di lanciare un'eccezione è che vi è un sacco di spese generali legate al lancio di un'eccezione.

Una cosa che l'articolo qui sotto 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 punto di partenza decente per le eccezioni all'interno di .NET: http://msdn.Microsoft.com/en-us/library/ms229030 (VS.80) .aspx

1
Sam

La sicurezza è confusa con il tuo esempio: non devi dire a un utente malintenzionato che esiste un nome utente, ma la password è sbagliata. Sono informazioni extra che non devi condividere. Dì solo "il nome utente o la password non sono corretti".

1
anon

Le eccezioni di lancio fanno sì che lo stack si srotoli, il che ha alcuni impatti sulle prestazioni (ammesso che i moderni ambienti gestiti sono migliorati). Ancora ripetutamente lanciare e catturare eccezioni in una situazione nidificata sarebbe una cattiva idea.

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

1
Arno

Alcune cose utili su cui riflettere quando si decide se un'eccezione è appropriata:

  1. quale livello di codice si desidera eseguire dopo che si è verificato il candidato all'eccezione, ovvero quanti livelli dello stack di chiamate dovrebbero rilassarsi. In genere si desidera gestire un'eccezione il più vicino possibile a dove si verifica. Per la convalida di nome utente/password, normalmente si gestiscono i fallimenti nello stesso blocco di codice, invece di far scoppiare un'eccezione. Quindi un'eccezione probabilmente non è 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 log degli errori? Non tutte le eccezioni vengono scritte in un log degli errori, ma è utile chiedere se questa voce in un log degli errori sarebbe utile - ad esempio, si proverebbe a fare qualcosa al riguardo, o sarebbe la spazzatura che si ignora.

0
Mike Kantor

Esistono due classi principali di eccezione:

1) Eccezione di sistema (ad es. Connessione al database persa) o 2) Eccezione utente. (es. Convalida dell'input dell'utente, 'password non corretta')

Ho trovato utile creare la mia User Exception Class e quando voglio lanciare un errore utente, voglio essere gestito in modo diverso (cioè l'errore di risorse mostrato all'utente), quindi tutto quello che devo fare nel mio gestore di 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
Crusty

"PasswordNotCorrectException" non è un buon esempio per l'utilizzo di eccezioni. Ci si deve aspettare che gli utenti ricevano le loro password in modo sbagliato, quindi non è un'eccezione IMHO. Probabilmente recupererai anche da esso, mostrando un bel messaggio di errore, quindi è solo un controllo di validità.

Eventuali eccezioni non gestite interromperanno l'esecuzione - cosa buona. Se stai restituendo codici falsi, nulli o di errore, dovrai gestire lo stato del programma tutto 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.

Naturalmente, si potrebbe causare lo stesso problema con dichiarazioni di catch vuote, ma almeno individuarle è più semplice e non richiede di capire la logica.

Quindi, come regola generale:

Usali ovunque tu non voglia o semplicemente non riesci a recuperare da un errore.

0
DanMan

È possibile utilizzare eccezioni un po 'generiche per tali condizioni. Ad es. ArgumentException è pensato per essere usato 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 desidera gestire in modo specifico è uguale al numero del tipo di eccezioni che il metodo deve generare. In questo modo, puoi determinare le eccezioni dettagliate che avrai.

A proposito, cerca sempre di fornire alcuni modi per gli utenti delle tue librerie di evitare eccezioni. TryParse è un buon esempio, esiste in modo che tu non debba usare int.Parse e prendere un'eccezione. Nel tuo caso, potresti voler fornire alcuni metodi per verificare se il nome utente è valido o se la password è corretta, in modo che i tuoi utenti (o tu) non debbano fare molta gestione delle eccezioni. Speriamo che questo si traduca in codice più leggibile e prestazioni migliori.

0
Serhat Ozgel

In definitiva, la decisione si riduce a stabilire se sia più utile gestire errori a livello di applicazione come questo utilizzando la gestione delle eccezioni o tramite il meccanismo del proprio home-roll come la restituzione dei codici di stato. Non penso ci sia una regola ferrea su quale sia meglio, ma vorrei prendere in considerazione:

  • Chi sta chiamando il tuo codice? Questa è una API pubblica di qualche tipo o una libreria interna?
  • Che lingua stai usando? Se è Java, ad esempio, lanciare un'eccezione (controllata) pone un carico esplicito sul chiamante per gestire in qualche modo questa condizione di errore, in contrasto con uno stato di ritorno che potrebbe essere ignorato. Potrebbe essere buono o cattivo.
  • Come vengono gestite altre condizioni di errore nella stessa applicazione? I chiamanti non vogliono occuparsi di un modulo che gestisce gli errori in modo idiosincratico diverso da qualsiasi altra cosa nel sistema.
  • Quante cose possono andare storte con la routine in questione, e come potrebbero essere gestite in modo diverso? Considera la differenza tra una serie di blocchi catch che gestiscono errori diversi e un passaggio a un codice di errore.
  • Hai informazioni strutturate sull'errore che devi restituire? Lanciare un'eccezione ti dà un posto migliore per mettere queste informazioni rispetto al semplice ritorno di uno stato.
0
eli