2009-08-20 2 views
15

This question is close to what I want to do, ma non proprio lì.Riesci a rilevare più di un tipo di eccezione con ogni blocco?

C'è un modo per semplificare il seguente codice?

private bool ValidDirectory(string directory) 
{ 
    if (!Directory.Exists(directory)) 
    { 
     if (MessageBox.Show(directory + " does not exist. Do you wish to create it?", this.Text) 
      == DialogResult.OK) 
     { 
      try 
      { 
       Directory.CreateDirectory(directory); 
       return true; 
      } 
      catch (IOException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (UnauthorizedAccessException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (PathTooLongException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (DirectoryNotFoundException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (NotSupportedException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
     } 
    } 

    return false; 
} 

Sembra uno spreco, e se poi voglio cambiare il modo segnalo un errore indietro per l'utente, o forse voglio registrare questi errori, o qualsiasi altra cosa, quindi ho avuto modo di cambiare 5 diversi prendere blocchi. Mi sto sfuggendo qualcosa, o è palesemente contro il riutilizzo del codice?

Sto solo cercando di essere (troppo) pigro?

risposta

23

È possibile utilizzare:

catch (SystemException ex) 
{ 
    if( (ex is IOException) 
     || (ex is UnauthorizedAccessException) 
// These are redundant 
// || (ex is PathTooLongException) 
// || (ex is DirectoryNotFoundException) 
     || (ex is NotSupportedException) 
    ) 
     lblBpsError.Text = ex.Message; 
    else 
     throw; 
} 
+1

Questo potrebbe essere semplificato un po '- rimuovere DirectoryNotFoundException e PathTooLongException perché (ex è IOException) restituirà true per loro –

+0

No non lo farà. L'operatore 'is' è molto preciso, non restituirà true per le sottoclassi, solo quella classe esatta. Ho pensato anche a questo, quindi l'ho testato su LINQPad. –

+1

Matthew, il seguente codice di test restituisce true. Non sono sicuro del motivo per cui hai ottenuto il risultato diverso ... try { throw new DirectoryNotFoundException(); } catch (Exception ex) { return (ex is IOException); } –

0

Si può fare

ex.GetType()

vedere http://msdn.microsoft.com/en-us/library/system.exception.gettype.aspx

EDIT

try    
{     
    Directory.CreateDirectory(directory); 
    return true;   
}    
catch (Exception ex)    
{ switch(ex.GetType()) 
     case ..... 
     case .......... 
    blBpsError.Text = ex.Message;    
} 
+1

Ciò richiederebbe l'eccezione "Eccezione", ma sì, questa è una soluzione. –

+0

Fai un interruttore sul tipo e il valore predefinito può semplicemente lanciarlo di nuovo? – qui

+0

Prenderesti Exception, ma potresti sempre lanciarlo di nuovo se non è un tipo che stai gestendo. – Keith

8

Se le eccezioni condividere una superclasse comune, allora si può solo prendere la superclasse .

+1

Non lo fanno. O meglio, non condividono una superclasse comune che nessuna altra possibile eccezione che voglio gestire non fa. –

0

si può prendere un'eccezione classe di base (tutte le eccezioni derivano da SystemException):

try 
{ 
    Directory.CreateDirectory(directory); 
    return true; 
} 
catch (SystemException ex) 
{ 
    lblBpsError.Text = ex.Message; 
} 

Ma poi si può finire per la cattura di eccezioni che non si desidera per la cattura.

+0

Il problema qui è che anche gli 'ArgumentException' sono 'SystemException', e io in particolare non voglio catturarli, perché quello è il mio problema, non gli utenti. –

+0

Quindi basta aggiungere un ramo catch che cattura ArgumentExceptions prima del ramo che cattura Exception (o il compilatore ottimizza i tentativi di cattura?) – RobV

+0

Li esegue in ordine. Sono abbastanza sicuro che molto non si ottimizza. Ma poi si torna ad avere tre dichiarazioni catch che puramente 'buttano;' –

3

Sì, si sta cercando di essere pigro, ma la pigrizia è una delle the virtues of a programmer, così va bene.

Per quanto riguarda la tua domanda: non c'è modo io sono a conoscenza, ma ci sono alcune soluzioni alternative disponibili:

  • dare al Eccezioni un antenato comune. Penso che questo non sarà possibile nel tuo caso, dal momento che sembra essere costruito.
  • Cattura l'eccezione più generica che puoi.
  • Spostare il codice di gestione nella propria funzione e chiamarlo da ogni blocco di cattura.
+0

Per la cronaca, tutti sono incorporati e così viene chiamato il metodo. –

+0

Thx, risposta aggiornata – soulmerge

2

Questo è fastidioso, e altre risposte hanno suggerito buoni soluzioni (io userei @ Lotfi).

Tuttavia questo comportamento è un requisito data la sicurezza del tipo di C#.

Immagino che si potrebbe fare questo:

try 
{ 
    Directory.CreateDirectory(directory); 
    return true; 
} 
catch (IOException, 
    UnauthorizedAccessException, 
    PathTooLongException, 
    DirectoryNotFoundException, 
    NotSupportedException ex) 
{ 
    lblBpsError.Text = ex.Message; 
} 

Ora che tipo è ex? Tutti hanno .Message perché ereditano System.Exception, ma provano ad accedere ad una delle loro altre proprietà e si ha un problema.

+0

Dovresti utilizzare il minimo comune denominatore. Puoi solo prendere le derivate di 'Exception', in modo che non abbia problemi reali. –

+0

Quindi, in questo caso, avresti finito con SystemException. Il compilatore può risolvere questo problema, quindi anche la sicurezza del testo non è giustificata. –

+0

Il compilatore poteva, e anche l'IDE poteva farlo, ma sarebbe stato alquanto oscurato per lo sviluppatore - quale intelligenza dovevano aspettarsi in ex? Posso vedere un modo in cui qualcosa del genere potrebbe funzionare - qualcosa come il supporto di co/contrravarianza in C# 4 – Keith

2

E 'anche importante notare che quando si cattura più di un tipo di eccezione dovrebbero essere ordinati per la maggior parte specifici al più generale.L'eccezione troverà il primo nella lista che corrisponde e genera quell'errore, nessuno degli altri errori verrà lanciato.

+0

Mi sono reso conto dopo aver postato che IOException era una classe base per alcuni di questi, ma è la prima nella lista sopra. Non importa comunque per questo codice, ma è un ottimo punto. –

0

Si potrebbe usare delegati, questo farà ciò che si vuole:

EDIT: semplificato un po '

static void Main(string[] args) 
{ 
    TryCatch(() => { throw new NullReferenceException(); }, 
     new [] { typeof(AbandonedMutexException), typeof(ArgumentException), typeof(NullReferenceException) }, 
     ex => Console.WriteLine(ex.Message)); 

} 

public static void TryCatch(Action action, Type[] exceptions, Action<Exception> catchBlock) 
{ 
    try 
    { 
     action(); 
    } 
    catch (Exception ex) 
    { 
     if(exceptions.Any(p => ex.GetType() == p)) 
     { 
      catchBlock(ex); 
     } 
     else 
     { 
      throw; 
     } 
    } 
} 

vostro particolare try/catch sarebbe:

bool ret; 
TryCatch(
    () => 
     { 
      Directory.CreateDirectory(directory); 
      ret = true; 
     }, 
    new[] 
     { 
      typeof (IOException), typeof (UnauthorizedAccessException), typeof (PathTooLongException), 
      typeof (DirectoryNotFoundException), typeof (NotSupportedException) 
     }, 
    ex => lblBpsError.Text = ex.Message 
); 

return ret; 
0

I Comprendere alcune di queste eccezioni potrebbe non essere prevedibile, ma se possibile provare a implementare la propria logica "preventiva". Le eccezioni sono costose, anche se in questo caso probabilmente non è un affare.

Ad esempio, utilizzare Directory.GetAccessControl (...) anziché affidarsi a una UnauthorizedAccessException da lanciare.

+0

Le eccezioni non sono così costose da risultare umane. Hai ragione, in questo caso non è sicuramente un affare e probabilmente non succederà mai, preferirei non andare in crash se lo facesse. –

2

Solo per completezza amor:

In VB, è possibile utilizzare condizionale la gestione delle eccezioni:

Try 
    … 
Catch ex As Exception When TypeOf ex Is MyException OrElse _ 
          TypeOf ex Is AnotherExecption 
    … 
End Try 

Tale blocco Catch otterrebbe inseriti solo per le eccezioni indicate - a differenza di C#.

Forse una versione futura di C# offrirà una funzionalità simile (dopo tutto, c'è un'istruzione IL specifica per quel codice).

MSDN: How to: Filter Errors in a Catch Block in Visual Basic

0

Partenza il The Exception Handling Application Block da EntLib. Articolano una metodologia di gestione delle eccezioni basata su criteri e configurazione molto buona che evita blocchi logici condizionali di grandi dimensioni.