Domanda

Sono sempre stato uno che ha sbagliato a prevenire le condizioni di eccezione, senza mai intraprendere un'azione a meno che non sia certo che non ci saranno errori. Ho imparato a programmare in C e questo era l'unico modo per fare davvero le cose.

Lavorando con C # Vedo spesso una programmazione più reattiva: prova a fare qualcosa e gestire le eccezioni. Per me questo sembra usare le eccezioni come dichiarazioni di controllo. Le prime volte che ho visto questo l'ho liquidato come cattiva pratica. Ma negli ultimi mesi l'ho visto dappertutto e devo solo chiedermi: è accettato / efficace o solo un'epidemia?

Aggiornamento: Per un po 'di chiarimenti, la maggior parte della gestione delle eccezioni che sto vedendo riguarda cose come

try
{
    //open file
}
catch
{
    //message box for file not found
}

o anche peggio

try
{
    //open xml
    //modify xml (100+ lines of code)
}
catch
{
    //message for 'unspecified error'
}

Capisco che ci sono momenti in cui la gestione delle eccezioni è molto buona da usare (come le connessioni al database) ma mi riferisco all'uso di eccezioni al posto di un controllo più "tradizionale". L'ho chiesto perché pensavo che questo stile di programmazione stesse usando le eccezioni come stampella anziché come metodo di recupero e volevo sapere se questo era solo qualcosa che avrei dovuto imparare ad aspettarmi nel mondo C #.

È stato utile?

Soluzione

Puoi certamente usare impropriamente le eccezioni è c #. A mio avviso, non dovresti mai ottenere ArgumentNullException, dal momento che dovresti sempre verificare prima null. Tuttavia, ci sono anche molti casi in cui non è possibile eseguire il range per uscire da un'eccezione. Tutto ciò che interagisce con "il mondo esterno" (la connessione a un server Web, database, ecc.) può generare un'eccezione.

Evita il più possibile, ma hai ancora bisogno della capacità di reagire a tutto il resto.

Altri suggerimenti

Come al solito, la risposta è "dipende", ma mi iscrivo al "fallimento veloce". filosofia in generale.

Preferisco usare try / finally (sans catch) a meno che non possa effettivamente fare qualcosa di utile per recuperare da un'eccezione in un particolare blocco di codice. Prendere ogni possibile eccezione non ne vale la pena. In generale, il fallimento veloce è preferibile al fallimento silenzioso.

Se, d'altra parte, sai come recuperare da una particolare eccezione, allora sì, vai a farlo.

Supponi di avere una libreria di trasferimento file. Probabilmente genererà un'eccezione se il trasferimento viene interrotto a causa di un timeout o di un errore di rete. È ragionevole. Sarai infastidito se la libreria fallisce in silenzio; la ricerca di un codice di ritorno è molto più soggetta a errori e non necessariamente più leggibile. Ma forse hai una regola aziendale per l'invio di un mucchio di file a un server che dovresti fare almeno 3 tentativi di trasferire il file prima di rinunciare e chiedere l'intervento dell'utente. In tal caso, la logica aziendale dovrebbe gestire l'eccezione, provare a ripristinare, quindi fare tutto ciò che dovrebbe fare quando la soluzione automatica fallisce (avvisare l'utente, pianificare un tentativo successivo o altro).

Se trovi il codice che fa questo:

try
{
    // do something that could throw
    // ...
}
catch {} //swallow the exception

o

catch { return null; }

Questo è probabilmente rotto. Certo, a volte il codice che chiami può generare un'eccezione che non ti interessa davvero. Ma spesso vedo che le persone lo fanno solo per non dover "gestire". l'eccezione a monte; la pratica rende le cose più difficili da eseguire il debug.

Alcune persone ritengono che le eccezioni a cascata nella catena della responsabilità siano cattive perché stai solo "sperando" qualcuno a monte " miracolosamente " sappi cosa fare. Quelle persone hanno torto. Il codice a monte è spesso l'unico posto in cui può sapere cosa fare.

Occasionalmente, proverò / catturerò e lancerò un'eccezione diversa, più appropriata. Tuttavia, quando possibile, una clausola di guardia è migliore. per esempio. if (argomento == null) lancia nuovo ArgumentNullException (); è meglio che consentire a NullReferenceException di propagare lo stack di chiamate, perché è più chiaro cosa è andato storto.

Alcune condizioni "non dovrebbero mai verificarsi" o " non sapevo che potesse accadere " dovrebbe probabilmente essere registrato (vedi, ad esempio, jboss logging), ma può essere ingoiato prima che abbattano la tua applicazione, almeno in alcuni casi.

ETA: è probabilmente rotto per prendere un'eccezione specifica e quindi visualizzare un messaggio di errore generale e ambiguo. Per il tuo secondo esempio sopra, mi sembra male. Per la prima volta, "File non trovato", che potrebbe essere più ragionevole (se effettivamente si rileva quella specifica eccezione, e non solo "tutto"), a meno che non si abbia un modo migliore di gestire tale condizione altrove. Le finestre di messaggio modali sono in genere un cattivo odore di "design di interazione" per me, ma questo è soprattutto il punto.

Ci sono alcuni casi in cui sei costretto a essere reattivo: controllare e operare non sempre funziona.

Accesso a un file su un disco: il disco funziona? esiste il file? posso ottenere l'accesso in lettura al file?

Accesso a un database: il server accetta connessioni? Le mie credenziali sono buone? Esistono gli oggetti del database? Le colonne sono denominate / digitate in modo appropriato?

Tutte queste cose possono cambiare tra il controllo e l'operazione.

Nei miei giorni COM C ++, ho imparato a non piegarmi all'indietro per gestire "possibile" errori - ovvero, non controllo abitualmente il valore restituito di ogni chiamata di funzione. Questo perché so di non riuscire a gestire condizioni sconosciute:

  • Non so quando fallirà o cosa significhi.

  • Non riesco a farlo accadere, quindi non posso testare il mio codice di gestione degli errori. Il codice non testato è codice che non funziona.

  • Non so cosa l'utente vorrebbe che facessi.

  • La gestione di errori di routine può consentire al programma di continuare a funzionare ma non in un modo affidabile e prevedibile di cui l'utente trarrebbe beneficio.

  • Fallimento rapido & amp; drammaticamente (anziché lentamente e sottilmente) non culla l'utente in un falso senso di sicurezza e offre al programmatore maggiori possibilità di diagnosticare il problema.

Ovviamente, non sto dicendo "non gestirò mai errori", sto dicendo "gestirò errori solo quando puoi aggiungere valore".

Gli stessi principi si applicano a C #. Penso che sia un'ottima idea catturare un'eccezione se puoi racchiuderla in un'eccezione più rilevante & amp; buttalo invece. E se sai con certezza in che modo il tuo utente trarrebbe vantaggio dalla gestione di un'eccezione, allora provaci. Ma per il resto, lascia perdere.

Credo che tu abbia ragione a dire che usare le eccezioni come dichiarazioni di controllo è una cattiva pratica. Le eccezioni in .Net sono lente. È sempre molto meglio fare i tuoi controlli piuttosto che usare le eccezioni per catturare "cattivo" valori, come Nulls, come Jon B menzionato .

L'ho scoperto in prima persona in un'app .Net 1.1 che ha creato file di testo delimitati di dati di laboratorio. Stavo usando le eccezioni per rilevare i campi con dati non validi, ma una volta sostituite le eccezioni con un codice di controllo appropriato, l'applicazione ha accelerato esponenzialmente.

L'indizio è nel termine "eccezione".

Se l'argomento null è una condizione commerciale valida , per definizione, un argomento null non è un'eccezione e dovrebbe essere testato prima di utilizzarlo. L'uso delle eccezioni per controllare la logica del programma in tali condizioni aziendali è una codificazione sciatta e l'autore deve essere schiaffeggiato ripetutamente con un eglefino bagnato fino a quando non si pente.

Tuttavia, a parte le prestazioni, testare sempre alla cieca, in questo esempio, argomenti nulli, non è meglio che intrappolare sempre un'eccezione quando viene generata.

Se dovrebbe essere impossibile che un argomento sia nullo , il test per null non dovrebbe essere eseguito, tranne nel livello più alto dell'applicazione in cui sono presenti eccezioni non gestite intrappolati, archiviati, inviati tramite e-mail al team di supporto e nascosti all'utente a cui deve essere visualizzato un messaggio adatto a titolo di scusa.

Una volta ho dovuto eseguire il debug di un'app in cui era stata gestita ogni possibile dannata eccezione, ed era letteralmente impossibile eseguire il debug. Doveva essere ritirato dalla produzione, trasformato in un ambiente di sviluppo, insieme al suo database e tutti i gestori strappati via. Dopodiché è stato banale.

L'immissione di un blocco di prova in C ++ è un'operazione costosa (in termini di cicli della CPU), quindi è necessario minimizzarli per tale motivo. Per C #, inserire il try-block è economico, quindi possiamo usarlo in un modo diverso.

Ora; catturare le eccezioni in C # è costoso, e dovrebbe comunque essere usato per casi eccezionali e non per la logica generale del programma, come altri hanno già affermato qui ...

C / C ++ può essere altrettanto reattivo. Quante volte hai visto qualcosa che ha tentato, ad esempio un fopen, di essere seguito da un controllo NULL? Il vantaggio delle eccezioni è che consentono di fornire più informazioni rispetto a un ritorno NULL o False.

La maggior parte delle operazioni che generano eccezioni in qualsiasi lingua che li supporta non è possibile " Test " prima mano per assicurarsi che l'operazione avrà successo.

Ora le eccezioni di runtime sono qualcosa di completamente diverso. Questi sono normalmente causati da dati non validi o da un uso improprio dei dati (divisione per zero). Questo tipo di eccezioni a mio avviso non dovrebbe mai essere colto come modo per gestirle.

È difficile discutere il miglior uso delle eccezioni senza una discussione generale sulla strategia delle eccezioni. Ad esempio, la tua strategia potrebbe essere una delle seguenti:

  • gestire tutte le eccezioni il più vicino possibile al punto di errore
  • registra un'eccezione, quindi ricodificala al chiamante
  • ripristina a uno stato di pre-eccezione e prova a continuare
  • traduce l'eccezione in un messaggio di errore appropriato e lo visualizza all'utente

La gestione delle eccezioni può spesso essere complicata dal fatto che diversi sviluppatori che lavorano allo stesso progetto potrebbero non avere nemmeno la stessa strategia o, peggio ancora, non sapere nemmeno che esiste. Quindi è importante che tutti in una squadra sappiano e capiscano qual è la strategia.

Per un buon punto di partenza sulla gestione e la strategia delle eccezioni, consultare il post sul blog di Ned Batchelder Eccezioni nella foresta pluviale .

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