Quanto assertivo dovrei essere nel gestire le eccezioni negli oggetti?

3

Ultimamente sto scrivendo in C # 4.0 ultimamente e sto cercando di scrivere il più magra possibile. Di conseguenza, non ho utilizzato i classici blocchi try/catch e using come spesso.

Capisco la funzione generale della garbage collection e della gestione delle eccezioni di .Net - Voglio il mio codice a prova di proiettile ed essere efficiente con gli oggetti, ma sto cercando di ottenere questo con la quantità minima di 'codice aggiuntivo' (dal momento che 'bloat' ha fatto incazzare la gente).

Per essere chiari, capisco che using() è fondamentale per rinunciare alle risorse per maniglie e altro codice e alla sua relazione all'interfaccia IDisposable .

Ma sto cercando di capire meglio quanto dovrebbe essere assertivo un programmatore negli oggetti di gestione delle eccezioni.

Ci sono posti chiave nel codice che diresti try/catch blocchi e / o using istruzioni sono indubbiamente necessarie? All'interno dei contesti della garbage collection e della gestione delle eccezioni, quali oggetti / scenari di uso comune consiglieresti esplicitamente?

Dovresti semplicemente creare un blocco catch per ogni possibile eccezione che un oggetto potrebbe attraversare? Quanto dovrebbero essere annidati? Ci sono dei passaggi che posso fare per raccogliere in modo proattivo i rifiuti (ad esempio, per contrastare l'esempio nei commenti di aspettare che Dispose() venga chiamato)?

    
posta one.beat.consumer 13.12.2011 - 20:07
fonte

6 risposte

9

using non è un "bloat", è assolutamente necessario liberare risorse racchiuse in oggetti IDisposable . using è compilato in try...finally , con una chiamata al metodo Dispose nella sezione finally . Ad esempio, quando StreamReader e StreamWriter vengono utilizzati per leggere e scrivere file, rispettivamente, il loro metodo Dispose chiude automaticamente il file. Un altro esempio è SqlConnection - il suo metodo Dispose deve essere chiamato per chiudere la connessione e un'istruzione using è un modo pulito per farlo.

Per quanto riguarda try...catch , il loro uso in .NET non differisce dalla gestione delle eccezioni in C ++ o Java. La gestione delle eccezioni riduce un po 'le prestazioni, ma il fatto che questo sia significativo può essere determinato solo analizzando il codice. La gestione delle eccezioni non deve essere utilizzata per modificare il flusso di controllo e non è consigliabile utilizzarlo per invertire le eccezioni Queste e altre importanti proprietà di gestione delle eccezioni si trovano nei libri di testo OOP / C ++ / Java / ...

    
risposta data 13.12.2011 - 20:20
fonte
3

try/finally e using sono problemi quasi totalmente ortogonali alle perdite di memoria. Dovresti usare using o try/finally quando hai risorse non di memoria, come un lucchetto, un handle o una connessione, e devi assicurarti che vengano pulite.

Ci sono pochi casi di common in .NET, dove è necessario prestare attenzione esplicita per evitare perdite di memoria. L'unico modo in cui finirai con una perdita di memoria se manterrai un riferimento a qualcosa di vecchio e non te ne accorgi; ci sono alcuni modi in cui questo può accadere, come se quella cosa avesse un evento da cui non hai cancellato l'iscrizione. Ma non c'è nulla di misterioso.

    
risposta data 13.12.2011 - 20:18
fonte
2

I livelli nidificati di try catch devono essere evitati se tutto ciò che fanno è nient'altro che registrare le eccezioni. Se l'oggetto supporta IDisposable, deve essere racchiuso nell'utilizzo del blocco.

Le eccezioni devono essere catturate per lo più sui punti di ingresso di root, invece di catturare e rilanciare a vari livelli. Rende solo il codice un po 'più complesso per analizzare le tracce dello stack.

Try-finally deve essere utilizzato solo se l'oggetto risorsa non supporta IDisposable, o per attivare alcune opzioni booleane o per sincronizzare i thread.

Non usare mai eccezioni come parte della logica, per esempio se rilevo eccezioni a segui il metodo b else segui il metodo c, qualcosa del genere. Un altro esempio include l'utilizzo dell'eccezione al posto del tipo restituito. Immagina che tu abbia il metodo ValidateUserPass che restituisce void e genera un'eccezione invece di restituire il risultato corretto. So che è discutibile, ma la logica non valida è un errore utente e l'eccezione dovrebbe essere utilizzata principalmente per errori di sistema o di rete.

Non lanciare eccezioni generiche Eccezione, ma usa la corretta classe incapsulata per questo.

A livello di root, cattura tutte le eccezioni perché deve essere registrato e, a livello intermedio, non rileva eccezioni a meno che tu non voglia continuare la logica in modo alternativo.

Ad esempio,

 try{
     connectToDevice()
     return;
 }catch(IOException ex){

 }
 connectToDatabase();

Qui, se rileverò tutte le eccezioni, mangerò o prenderò un'eccezione errata se inserirò tutto il blocco try. Qui, nel caso in cui la mia connessione al dispositivo fallisca, voglio collegarmi al database e lavorare in modalità offline. Questo fa parte della logica solo se il dispositivo non riesce a connettersi.

Ma se l'utente ha inserito un null come stringa di indirizzo, non dovrebbe essere catturato qui perché non si tratta di un errore nella connessione del dispositivo. La mia eccezione puntatore nullo dovrebbe essere rilevata a livello di root e non in questo codice.

Per la registrazione, cattura tutte le eccezioni.

Per un ramo alternativo di esecuzione, prendi solo un tipo di eccezione.

    
risposta data 13.12.2011 - 20:35
fonte
1

using è solo permesso dal linguaggio per le situazioni in cui dovresti quasi certamente farlo, cioè classi che implementano IDisposable . Sei libero di scrivere codice che implementa IDisposable inutilmente se lo desideri, ma quasi tutte le librerie lo faranno solo per un motivo - perché l'oggetto deve essere ripulito. Puoi scrivere la logica di smaltimento autonomamente se vuoi, ma non c'è alcun vantaggio perché using fa esattamente la stessa cosa, è solo più chiaro.

La tua definizione di "bloat" è interessante. Mentre capisco che il noioso codice idraulico è indesiderabile, questo lo rende un candidato per il walling in una classe base, non escludendolo del tutto. Se il codice noioso si sta diffondendo, può essere un'indicazione che stai violando DRY e dovrebbe mirare al refactoring in modo che tu specifichi il comportamento una sola volta per tutto ciò che ne ha bisogno.

    
risposta data 13.12.2011 - 20:26
fonte
1

Per rispondere alla parte della tua domanda su quali eccezioni dovresti prendere, dai un'occhiata a questo articolo:

Come progettare le gerarchie di eccezioni

Nonostante il titolo, è molto utile per comprendere i diversi tipi di eccezioni. Esistono tre tipi e devono essere gestiti in tre modi diversi:

  • Errore di utilizzo . Ciò è dovuto a un errore nella codifica: correggi il tuo codice .
  • Errore logico . Una condizione inevitabile, come il file non trovato - gestisce l'eccezione .
  • Errore di sistema . Un errore di sistema come memoria insufficiente - spegni il programma .

(File non trovato è inevitabile perché anche se chiami File.Exist (), è possibile che qualcuno possa cancellare il file solo prima di accedervi. Quindi, per essere veramente corretto, devi comunque gestiscilo.)

L'unico caso in cui è assolutamente necessario cercare / catturare i blocchi è per errori logici. In questi casi, non puoi garantire di evitare l'eccezione, quindi devi gestirlo.

    
risposta data 13.12.2011 - 21:26
fonte
1

Ogni eccezione viene gestita. Devi solo scegliere se uccidere il tuo programma o no, e se no, cosa vuoi fare dopo. Scrivo regolarmente codice che genera eccezioni su e giù nello stack, recupera quelli da cui vuole recuperare e ha un semplice metodo principale:

public static int main(string args[])
{
    try {
        DoSomethingUseful(args);
        return 0;
    } catch (Exception e) {
        Console.Write("Uncaught exception percolated to top level: " + e);
    }
    return 1;
}
    
risposta data 14.12.2011 - 02:29
fonte

Leggi altre domande sui tag