2010-06-17 7 views
7

Lavorare con .NET framework Ho un servizio con una serie di metodi che possono genera diversi tipi di eccezioni: MyException2, MyExc1, Exception ... Fornire lavoro adeguata per tutti metodi, ciascuno dei quali contiene le seguenti sezioni:Come evitare eccezioni catture copia-incolla in .NET

[WebMethod] 
void Method1(...) 
{ 
    try 
    { 
     ... required functionality 
    } 
    catch(MyException2 exc) 
    { 
     ... process exception of MyException2 type 
    } 
    catch(MyExc1 exc) 
    { 
     ... process exception of MyExc1 type 
    } 
    catch(Exception exc) 
    { 
     ... process exception of Exception type 
    } 
    ... process and return result if necessary 
} 

E 'molto noioso per avere esattamente stesse cose in ogni metodo di servizio (ogni metodo ha diversi set di parametri) esattamente stesse eccezioni funzionalità di elaborazione ...

Esiste la possibilità di "raggruppare" queste sezioni di cattura e utilizzare solo una linea (qualcosa di simile alle macro C++)? Probabilmente qualcosa di nuovo in .NET 4.0 è correlato a questo argomento?

Grazie.

P.S. Ogni pensiero è benvenuto.

risposta

6

Se la gestione delle eccezioni è esattamente lo stesso in tutti i metodi, si potrebbe fare qualcosa di simile:

void CallService(Action method) 
{ 
    try 
    { 
     // Execute method 
     method(); 
    } 
    catch(MyException2 exc) 
    { 
     ... process exception of MyException2 type 
    } 
    catch(MyExc1 exc) 
    { 
     ... process exception of MyExc1 type 
    } 
    catch(Exception exc) 
    { 
     ... process exception of Exception type 
    } 
} 

Poi, si può solo riscrivere il codice del client di fare:

int i = 3; 
string arg = "Foo"; 
this.CallService(() => this.Method1(i)); 
this.CallService(() => this.Method2(arg, 5)); 

Questo permette ai metodi Method1 e method2 di essere semplicemente:

void Method1(int arg) 
{ 
    // Leave out exception handling here... 
    ... required functionality 
    ... process and return result if necessary 
} 

void Method2(string stringArg, int intArg) 
{ 
    // Leave out exception handling here... 
    ... required functionality 
    ... process and return result if necessary 
} 
+0

Ho avuto un'idea simile, ma il problema è che ogni metodo ha un diverso insieme di parametri (mi dispiace, non l'ho menzionato nella domanda originale e nelle specifiche aggiunte di recente). Ecco un esempio di firme dei metodi: string Method1 (string); void Method2 (string, int, int), ecc ... – Budda

+0

@Budda: non importa - funzionerà bene con quello, dal momento che lambda può passare in tutti gli argomenti, il modo in cui l'ho scritto. –

+0

@Budda: ho modificato la mia risposta per dimostrare :) Funziona con qualsiasi numero di argomenti nei metodi ... –

-3

so che è cattiva pratica, ma se avete la stessa identica gestione degli errori in ogni dichiarazione di cattura, quando perché non utilizzare l'ultima istruzione catch come una cattura tutti?

Questo, naturalmente, assume tutte le eccezioni ereditano da Exception.

+0

-1: se sai che è una cattiva pratica, allora perché raccomandarlo? –

+0

Non lo sono - sono solo curioso di sapere perché avresti la stessa identica gestione degli errori in più istruzioni catch, poiché il punto di avere più catture è che gestisci le eccezioni in modo diverso ... –

+0

Ho esattamente la stessa gestione per fornire lo stesso errore di elaborazione per tutti i metodi. – Budda

5

Perché non limitarsi a inserire il codice in un metodo di supporto per farlo per te (puoi aggiungere le nuove eccezioni di cui hai bisogno in futuro allo HandleException, cosa che lo rende abbastanza scalabile)?

try 
{ 
    ... required functionality 
} 
catch (Exception e) 
{ 
    HandleException(e); 
    throw; // only if you need the exception to propagate to caller 
} 


private void HandleException(Exception e) 
{ 
    if (e is MyException2) 
    { 
     ... process exception of MyException2 type 
    } 
    else if (e is MyExc1) 
    { 
     ... process exception of MyExc1 type 
    } 
    else 
    { 
     ... process exception of Exception type 
    } 
} 
+0

dcp, perché si rispedisce l'eccezione dopo la chiamata a HandleException (e)? – Budda

+1

Questo modello violerebbe in modo provocatorio la regola di analisi del codice di Microsoft "Non rilevare tipi di eccezioni generali" http://msdn.microsoft.com/en-us/library/ms182137(v=VS.90).aspx – chilltemp

+3

@chilltemp - Vieni , il non ripetere la tua regola ha una precedenza molto più alta. – ChaosPandion

2

Guarderei da vicino cosa stai facendo per "elaborare" queste eccezioni. Ci sono buone probabilità che non ti servano affatto blocchi di cattura e che dovresti consentire le propagazioni delle eccezioni.

+0

Questi metodi sono [WebMethod] del servizio web e dovrebbero SEMPRE restituire qualcosa di significativo al client. Tutte le sezioni di gestione delle eccezioni generano l'oggetto "error", che viene serializzato in formato XML e inviato al client. Il tuo suggerimento mi ha dato un'idea per ottimizzare la creazione di oggetti di errore ... può essere implementata con un modello di fabbrica ... nel modo seguente: catch (Exception exc) { Errore MyError = MyError.GenerateErrorObject (exc); ... processo (errore); } Grazie. – Budda

+0

@Budda: 1) È bello sapere. Dillo così la prossima volta. 2) Dovresti lanciare 'SoapException' con il set di dettagli per restituire un errore SOAP. –

+0

Ho bisogno di inviare un messaggio in DETERMINATO formato personalizzato, e l'eccezione SOAP non si adatta :) – Budda

1

In un lampo improvviso di geekiness (e per mostrare ciò che si poteva, ma molto probabilmente non dovrebbe e non si vuole fare): è possibile rendere il tutto più compositivo e riusabile generando funzioni al volo che contengono l'eccezione logica di movimentazione:

static class ExceptionHandlerExtensionMethods 
{ 
    // extend to Func<T> as desired 
    public static Action Catching<T>(this Action what, Action<T> handler) 
     where T : Exception 
    { 
     return() => 
     { 
      try 
      { 
       what(); 
      } 
      catch (T ex) 
      { 
       handler(ex); 
      } 
     }; 
    } 
} 

Ora è possibile implementare e funzioni di gestione specifiche riutilizzo eccezione di tipo da qualche parte, e li compongono nella gestione delle eccezioni di qualche altro metodo.

Per rimuovere la ridondanza, è possibile scrivere una funzione di supporto che aggiunge le funzioni "tipiche" al gestore delle eccezioni a che cosa mai si desidera chiamare e chiamare questo metodo decorato invece.

0

Utilizzando il metodo Callservice da Reed Copsey:

void DoSomething(object param1, int param2) 
{ 
    this.CallService(() => 
    { 
     // work with param1 and param2 here 
    } 
} 

Per i casi in cui è necessario restituire un valore, potrebbe essere necessario duplicare Callservice per restituire un parametro di tipo.

T CallService<T>(Func<T> callback) { /* ... */ } 
1

Ho fatto quanto segue in una situazione simile. Vi mostrerò la tecnica a due passi ...


Fase 1. Creare un metodo che fornisce un contesto di esecuzione specifica per altro codice:

// this static method is responsible for setting up a context for other code 
// to run in; specifically, it provides the exception handling "plumbing": 
public static void guarded(Action action) 
{      // ^^^^^^^^^^^^^ 
    try    // actual code to be executed 
    { 
     action(); 
    } 
    catch (SomeExceptionA ea) 
    { 
     // handle exception... 
    } 
    catch (SomeExceptionB eb) 
    { 
     // handle exception... 
    } 
    // etc. 
} 

Fase 2 Applicare questo contesto a qualsiasi parte di codice:

Successivamente, è sufficiente "avvolgere" questa gestione delle eccezioni attorno all'atto codice sessuali nelle metodi:

public void MethodA() 
{ 
    guarded(() => // <-- wrap the execution handlers around the following code: 
    { 
     // do something which might throw an exception... 
    }); 
} 

public void MethodB() 
{ 
    guarded(() => 
    { 
     // do something which might throw an exception... 
    }); 
} 

Sommario:

L'idea generale è quello di scrivere una funzione (guarded nell'esempio precedente), che stabilisce un contesto di esecuzione specifica per altro codice da eseguire. (Il contesto in questo esempio fornisce la gestione delle eccezioni.) Il codice che deve essere eseguito in questo contesto è fornito come funzione lambda. Si potrebbe anche adattare la funzione di creazione del contesto in modo che il codice nella funzione lambda possa restituire un valore.

+0

Come suggerito da Reed Copsey. Ma grazie comunque. – Budda

+1

Oops. Scusa per il quasi duplicato, sono riuscito in qualche modo a ignorare la sua risposta. ** MA nota una sottile differenza nelle nostre due risposte: Nella sua risposta, la funzione esterna è esplicitamente avvolta attorno al metodo effettivo _ dal chiamante del metodo_. Suggerirei che ci sono anche casi in cui è più appropriato che i tuoi metodi gestiscano il loro contesto da soli. Potrebbero sapere meglio del chiamante quali eccezioni possono/devono essere gestite. – stakx

Problemi correlati