Come decidere tra l’uso se / else vs try / catch?

Durante la scrittura del codice, come si può decidere tra l’uso di if / else o try / catch? Ad esempio, nel controllare un file, dovrebbe essere basato su if / else (if (File.Exists)) o su un blocco try / catch?

Ad esempio, scrivere su un file può essere gestito tramite un blocco if / else per creare un file e quindi scrivere su di esso, o un try / catch con l’assunto che il file esista. Quali considerazioni ci sono nella scelta?

Grazie

Non dovresti mai usare try / catch per il controllo del stream.

Generare un’eccezione è un’azione estremamente costosa. Se / else è molto più veloce e più pulito.

Dovresti sempre usare try / catch quando lavori con i file, perché lo stato di un file può cambiare al di fuori del tuo programma.

Considera il seguente bit di codice:

if(File.Exists("file.txt")) File.Delete("file.txt") 

Il file potrebbe essere stato eliminato da un altro processo subito dopo l’istruzione if, prima della chiamata Delete() . Quando si tenta di eliminarlo, viene sollevata un’eccezione.

Quando si lavora con i file ci sono anche molte altre cose da considerare, che potresti non essere in grado di catturare con ifs, ad esempio il file si trova su una connessione di rete che non è disponibile, i diritti di accesso che cambiano, l’errore del disco fisso ecc.

Queste cose sono fuori dal controllo del tuo programma, quindi dovresti avere a disposizione gestori di eccezioni.

Se pensi che l’operazione dovrebbe normalmente avere successo, allora try/catch può essere più facile da leggere. Soprattutto, se ci sono molte ragioni per il fallimento (più blocchi di catch ).

Altrimenti, se a volte riesce e talvolta fallisce – e lo fa per una ragione specifica, usa if/else (questo è noto come gestione delle eccezioni strutturate).

Alcune persone sottolineano come la gestione delle eccezioni con try/catch possa richiedere molto tempo. Tendo a leggere consigli del genere seguendo le seguenti linee: Non farlo in un anello chiuso stretto, se il tuo profilo indica un problema di prestazioni. Quando codifichi la tua prima bozza, non preoccuparti di pensare a un’ottimizzazione a questo livello!

Usa try / catch quando qualcosa di inaspettato (un’eccezione) potrebbe accadere e vuoi fare qualcosa di speciale al riguardo, come:

 try { //Do something that might rise an exception, say a division by 0 //this is just an easy example as you might want to check if said value is not 0 //before dividing int result = someNumberOfMine / anUnsafeNumber; } catch { //if something unexpected happened, do a special treament } 

Nel tuo caso particolare, ti consiglio di utilizzare File.Exists per verificare la presenza del file invece di utilizzare un blocco try / catch poiché la presenza o meno del file può essere verificata prima di fare qualcosa con esso.

Solo per mettere l’argomento a rest (sì, la domanda è stata posta 8 mesi fa, ma internet lo sa sempre!), Ho deciso di eseguire un piccolo test per il quale è più efficiente se sei abbastanza sicuro di non avere un eccezione – per esempio, la parte “else” sta per accadere solo lo 0,001% delle volte. A quanto pare, se non si deve mai prendere qualcos’altro, il try-catch è circa il 4% più veloce (sulla mia macchina, comunque). Ecco il codice e i risultati di accompagnamento:

CASO 1: se-altro:

 var startTime = DateTime.Now; int bazillion = 100000000; int[] list = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; for (int i = 0; i < bazillion; i++) { for (int k = 0; k < list.Length; k++) { if (k >= 0) { list[k] = i; } else { // do nothing. } } } var executionTime = DateTime.Now - startTime; Debug.WriteLine (executionTime.TotalMilliseconds); 

Tempi di esecuzione (millisecondi) su 5 esecuzioni: 7441.4256, 7392.4228, 7545.4316, 7531.4308, 7323.4188.
Media = 7446,82592 millisecondi

CASO 2: try-catch:

 var startTime = DateTime.Now; int bazillion = 100000000; int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; for (int i = 0; i < bazillion; i++) { for (int k = 0; k < list.Length; k++) { try { list[k] = i; } catch (Exception e) { // do nothing } } } var executionTime = DateTime.Now - startTime; Debug.WriteLine(executionTime.TotalMilliseconds); 

Tempi di esecuzione (millisecondi) su 5 esecuzioni: 7258.4152, 7137.4083, 7070.4044, 7052.4033, 7120.4073 Media = 7127.8077 millisecondi

Conclusione (basata su questo esempio piuttosto semplicistico, il chilometraggio effettivo può variare, ecc.) :
In termini di numeri puri, se sei abbastanza sicuro che l'eccezione / il caso non si verificherà, try-catch è circa il 4% più veloce di dover eseguire la clausola "if" ogni volta.

La gestione eccezionale dovrebbe essere eseguita o utilizzata solo in casi eccezionali.

In uno scenario che dipende dal fatto che esista o meno un file, non ha senso utilizzare try catch block quando semplicemente è ansible farlo

 if(File.Exists(path)) { } else { } 

Una gestione eccezionale causa un notevole File.Exists(path)) delle prestazioni, quindi prova a ridurre al minimo i casi eccezionali applicando più controlli nel codice come se File.Exists(path))

in generale dipende

Per gli articoli basati su file, quasi sempre si desidera provare l’operazione e gestire gli errori anziché controllare prima. il motivo è che il file system è una risorsa condivisa e non è ansible garantire che dopo che file.exists restituisce true il file esiste come altri processi potrebbero averlo eliminato nel frattempo.

Come alcune risposte hanno già indicato, dipende.

Se / else vengono utilizzati per il controllo del stream, ma lo possono anche Eccezioni con il vantaggio aggiuntivo di rilevare un errore che si verifica. Ma come ha sottolineato Turowicz, è considerato una ctriggers pratica per molte persone, per provare / catturare più del Minimo di quello che devi.

Puoi sempre leggere questi articoli di Ned Batchelder (parla di codici di ritorno, in alternativa all’utilizzo di eccezioni) e Joel Spolsky (perché non gli piace programmare con le eccezioni) per avere un’idea di cosa pensano le eccezioni e quindi rendere il tuo pensa.

Solo un pensiero … una delle risposte era che dovresti fare un tentativo di cattura se, ad esempio, hai possibilità di divisione per zero. Mi chiedo perché? Hai il controllo qui, puoi controllare prima di dividere e agire. Se è zero, non è necessario eseguire la divisione, ma eseguire un’altra logica.

Utilizzerei solo il try catch nel caso in cui non si è in controllo o non è ansible (o non ha senso) controllare le cose in anticipo (aprire un file, …).

Nel tuo caso, userei File.Exists e try / catch. File.Exists come controllo aziendale (non è necessario aprirlo quando non esiste), try / catch per rilevare eventuali eccezioni che potrebbero verificarsi durante l’apertura del file.

Quando non ci si aspetta che il file esista, verificare prima la presenza. Ma quando il file mancante è inusuale, dovresti indicare questo stato eccezionale con un’eccezione.

Quindi l’idea di base è: cercare di evitare eccezioni in circostanze prevedibili.

In generale dovresti fare entrambe le cose. prova / cattura per evitare situazioni eccezionali (il file è stato improvvisamente cancellato da un altro thread). E se / else gestire non eccezionale (controllare se il file esiste). Try / catch è relativamente più lento di un solito se / else quindi non vale la pena usarlo per tutto.

L’adagio “Le eccezioni dovrebbero essere eccezionali” è utile quando si prendono questi tipi di decisioni. Il principale è che dovresti gestire le situazioni note con il stream del programma standard (cioè se le istruzioni) in modo che le eccezioni rappresentino situazioni impreviste (es. Bug).

Naturalmente, come ogni regola, è lì per essere rotto.

Inoltre, non mi preoccuperei troppo per l’impatto sulle prestazioni della gestione delle eccezioni. Il sovraccarico è praticamente trascurabile in tutte le situazioni tranne che in quelle estreme.

Per questo conosci il problema dell’uscita del file. Quindi preferisci se altro.

Se non conosci il problema, meglio usare try catch.

suggerisco entrambi, come di seguito

 try { if(File.Exists(path)) { do work } else { messagebox.show("File Path Not Exit"); } } catch(Exception e) { messagebox.show(e); } 

prende tutti gli errori, che non pensiamo.

Come è ovvio da tutte le risposte, non esiste un modo standard / approvato per decidere se utilizzare l’una o l’altra. Se fatto correttamente, entrambi i metodi saranno efficaci. Se fatto in modo errato, entrambi i metodi saranno inefficienti.

Preferisco le istruzioni if ​​/ else quando è significativo (cioè all’interno delle mie funzioni) e Try blocks quando chiamo funzioni che non ho controllo.

Nell’esempio precedente, File.Existe () sarebbe sufficiente se si controlla il file (ovvero non è probabile che altri programmi lo manipolino) ma non sufficiente se esiste la possibilità che il file scompaia tra il controllo e l’utilizzo.

Le operazioni sui file, in generale, sono meglio gestite con eccezioni nella maggior parte dei casi, secondo la mia esperienza, perché queste operazioni molto file, in c #, stanno sollevando eccezioni. Non restituiscono codici di errore che potrebbero essere controllati con istruzioni if.