Gestire le eccezioni, è un buon modo?

Stiamo lottando con una politica per gestire correttamente le eccezioni nella nostra applicazione. Ecco i nostri obiettivi (riassunti):

  • Gestire solo eccezioni specifiche.
  • Gestisci solo le eccezioni che puoi correggere
  • Accedi solo una volta.

Siamo usciti con una soluzione che comporta un’eccezione specifica dell’applicazione e funziona come questo in un pezzo di codice:

try { // Do whatever } catch(ArgumentNullException ane) { // Handle, optinally log and continue } catch(AppSpecificException) { // Rethrow, don't log, don't do anything else throw; } catch(Exception e) { // Log, encapsulate (so that it won't be logged again) and throw Logger.Log("Really bad thing", e.Message, e); throw new AppSpecificException(e) } 

Tutte le eccezioni vengono registrate e quindi trasformate in AppSpecificException in modo che non vengano registrate di nuovo. Alla fine raggiungerà l’ultimo gestore di eventi che si occuperà di esso se necessario.

Non ho molta esperienza con i modelli di gestione delle eccezioni … È un buon modo per risolvere i nostri obiettivi? Ha dei grossi inconvenienti o grossi avvertimenti rossi?

Nota: uno degli svantaggi di questo è che dopo la prima cattura perdi la capacità di gestire un’eccezione specifica (se chiami un metodo che chiama un altro metodo e il secondo lancia un’eccezione non sei in grado di gestirlo) ma Ho scoperto che non l’ho mai fatto in nessun modo … gestisco solo eccezioni con un solo livello di profondità …

Se si registra l’eccezione troppo vicino al tempo in cui viene generata per la prima volta, non si registrerà l’intera traccia dello stack.

Gestire le eccezioni (ovvero correggerle), il più vicino ansible a quando sono state lanciate. Raccogli informazioni sul contesto il prima ansible a quando sono state lanciate. Ma consenti alle eccezioni di propagarsi fino a dove possono essere effettivamente gestite. La registrazione è un tipo di gestione di ultima istanza, pertanto dovrebbe verificarsi nei livelli esterni dei sottosistemi dell’applicazione.

Ciò dovrebbe eliminare la necessità di un’eccezione specifica dell’applicazione utilizzata come marcatore per non registrare un’eccezione che non avrebbe dovuto essere catturata per cominciare.

Non registrare un’eccezione e quindi rilanciarla: è responsabilità del chiamante gestire / registrare eventuali eccezioni generate.

Rileva solo un’eccezione per gestirlo (ad esempio per collegarlo) o aggiungi informazioni specifiche sul contesto.

Questo è un approccio abbastanza comune per risolvere il problema di gestione delle eccezioni (da più specifico a meno specifico).

Tieni a mente che avere un’eccezione generica ApplicationSpecific per catturare tutto ciò che accade in quell’applicazione / metodo non è una grande idea se vuoi rilevare problemi specifici. Alla fine prova a estenderlo con eccezioni più specifiche.

Rimandare le eccezioni è buono, è meglio dichiarare il metodo per generare alcune eccezioni e consentire ai chiamanti di gestirle. In questo modo dovrai creare meno codice e potresti centralizzare alcuni controlli.

Prima opzione per risolvere il problema di tracciabilità dello stack:

 class AppSpecificException : ApplicationException { public string SpecificTrace { get; private set; } public string SpecificMessage { get; private set; } public AppSpecificException(string message, Exception innerException) { SpecificMessage = message; SpecificTrace = innerException.StackTrace; } } 

Ho dovuto scrivere un esempio per capire la domanda e controllare il problema dello stacktrace, questo è il codice per me, porre attenzione al metodo button2_click, finalmente la mia casella di testo mostra la stringa di crash e lo stacktrace:

  private String internalValue; private void Operation1(String pField) { if (pField == null) throw new ArgumentNullException("pField"); internalValue = pField; } private void Operation2(Object pField) { if (pField == null) throw new ArgumentNullException("pField"); internalValue = Convert.ToInt32(pField).ToString(); } private void Operation3(String pField) { if (pField == null) throw new ArgumentNullException("pField"); internalValue = pField; Operation2(-1); } /// AppSpecificException. private void button1_Click(object sender, EventArgs e) { try { Operation1("One"); Operation2("Two"); Operation3("Three"); MessageBox.Show(internalValue); } catch (ArgumentNullException ex) { textBoxException.Text = ex.Message + (char) 13 + (char) 10 + ex.StackTrace; } catch (AppSpecificException ex) { //textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace; throw; } catch (Exception ex) { textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace; throw new AppSpecificException("crash", ex); } } private void button2_Click(object sender, EventArgs e) { try { button1_Click(sender, e); } catch (AppSpecificException ex) { textBoxException.Text = ex.SpecificMessage + (char) 13 + (char) 10 + ex.SpecificTrace; } } 

Cerca di evitare la creazione di una nuova eccezione e di rilanciare, poiché il lancio di un’eccezione imposta la traccia dello stack nel punto in cui è stata generata l’eccezione. Fai un semplice tiro. Vedi troppo riutilizzo sul blog di Eric Lippert.

Consiglierei di mettere più in considerazione i modelli che vuoi usare per “passare”

Se i tuoi schemi di gestione si riducono al log o al rethrow, alla fine verrà registrato l’errore di rethrown. Quindi, alla fine, è solo la registrazione degli errori. Se stai usando ASP.NET usa elmah così almeno il tuo codice non è coperto con la piastra di prova try / catch-and-log.

Ci sono solo alcuni modi per “gestire” gli errori che non terminano con la semplice registrazione.

Riprovare. (Fai attenzione ai loop infiniti)

Aspetta e riprova.

Prova una tecnica diversa ma equivalente (non riesci a connetterti su http? Prova a connetterti su https).

Stabilire le condizioni mancanti (creare la cartella che ha gettato FolderNotFoundException)

Ignora l’errore: pensaci due volte, ha senso solo quando l’errore non è realmente un problema, come se una libreria di terze parti ti avvisasse di una condizione che non si applica.

Una buona soluzione sulla gestione delle eccezioni sta usando Interception. Tuttavia, è necessario verificare se questo modello può essere applicato all’applicazione in base all’architettura: l’intercettazione richiede un contenitore.

Il principio è di ridimensionare la gestione delle eccezioni al di fuori dei metodi utilizzando attributi (personalizzati) su di essi e quindi utilizzare il contenitore per inizializzare le istanze. Il contenitore proxy tali istanze per riflessione: i suoi istanze chiamate intercettori. Dovrai semplicemente chiamare i tuoi metodi come al solito tramite queste istanze e lasciare che il meccanismo di intercettazione faccia il lavoro che hai codificato prima o dopo il metodo.

Si noti che è ansible aggiungere try catch nei metodi per gestire le eccezioni specifiche non gestite negli intercettori.

Intercettazione di unità: http://msdn.microsoft.com/en-us/library/dd140045.aspx