2009-08-20 5 views
15

This question is close to what I want to do, pero no del todo allí.¿Puede atrapar más de un tipo de excepción con cada bloque?

¿Hay alguna manera de simplificar el siguiente código?

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; 
} 

Parece un desperdicio, y si más adelante desea cambiar la forma puedo informar de un error de nuevo al usuario, o tal vez quiero registrar estos errores, o lo que sea, entonces tengo que cambiar 5 diferentes atrapar bloques. ¿Me estoy perdiendo algo, o esto es descaradamente contra la reutilización del código?

¿Solo trato de ser (demasiado) vago?

Respuesta

23

que puede utilizar:

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

Esto podría simplificarse un poco: elimine DirectoryNotFoundException y PathTooLongException porque (por ejemplo, IOException) devolverá true para ellos –

+0

No, no lo hará. El operador 'is' es muy exacto, no volverá a ser verdadero para las subclases, solo esa clase exacta. Pensé esto también, luego lo probé en LINQPad. –

+1

Matthew, el siguiente código de prueba devuelve verdadero. No estoy seguro de por qué tienes el resultado diferente ... try { throw new DirectoryNotFoundException(); } catch (Exception ex) { return (ex is IOException); } –

0

Usted puede hacer

ex.GetType()

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

EDITAR

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

Eso requeriría atrapar 'Exception', pero sí, esa es una solución. –

+0

¿Haces un cambio en el tipo y por defecto puedes lanzarlo de nuevo? – qui

+0

Detectarías Excepción, pero luego siempre podrías lanzarla de nuevo si no es un tipo que estés manipulando. – Keith

8

Si las excepciones comparten una superclase comunes a continuación, puedes coger la superclase .

+1

No lo hacen. O más bien, no comparten una superclase común que ninguna otra posible excepción que deseo manejar no lo hace. –

0

se puede coger una excepción de clase base (todas las excepciones se derivan de SystemException):

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

Pero entonces usted puede terminar la captura de excepciones que no desea atrapar.

+0

El problema aquí es que las 'ArgumentException' s son' SystemException's también, y específicamente no quiero capturarlas, porque ese es mi problema, no los usuarios. –

+0

Luego, solo agrega una rama de captura que captura ArgumentExceptions antes de la rama que detecta Exception (¿o el compilador optimiza try catch?) – RobV

+0

Las ejecuta en orden. Estoy bastante seguro de que al menos no se optimiza. Pero luego vuelve a tener tres declaraciones catch que simplemente 'throw;' –

3

Sí, usted está tratando de ser perezoso, pero la pereza es uno de the virtues of a programmer, y eso es bueno.

cuanto a su pregunta: No hay manera que soy consciente de, pero hay algunas soluciones disponibles:

  • dar los Excepciones un ancestro común. Creo que esto no será posible en tu caso, ya que parecen estar integrados.
  • Captura la excepción más genérica que puedas.
  • Mueva el código de manejo a su propia función y llámelo desde cada bloque catch.
+0

Para el registro, todos ellos están incorporados, y así es el método que se llama. –

+0

Thx, respuesta actualizada – soulmerge

2

Esto es molesto, y otras respuestas han sugerido buenas soluciones (usaría @ Lotfi's).

Sin embargo, este comportamiento es un requisito dado el tipo de seguridad de C#.

supongo que se podría hacer esto:

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

Ahora qué tipo es ex? Todos tienen .Message porque heredan System.Exception, pero intenta acceder a cualquiera de sus otras propiedades y tienes un problema.

+0

Tendría que usar el mínimo común denominador. Solo puede capturar derivadas de 'Exception', por lo que no tiene problemas reales. –

+0

Entonces, en este caso, terminarías con una SystemException. El compilador puede resolver esto, por lo que la seguridad de tipo tampoco está comprometida. –

+0

El compilador podría, y el IDE podría hacerlo también, pero sería algo oscurecido para el desarrollador, ¿qué intellisense deberían esperar de él? Sin embargo, puedo ver que algo así podría funcionar: algo así como el soporte de co/contravarianza en C# 4 – Keith

2

También es importante tener en cuenta que cuando se capturan más de un tipo de excepción, la mayoría debe especificarla a la más general.La excepción encontrará la primera en la lista que coincida y arroje ese error; ninguno de los otros errores se arrojará.

+0

Me di cuenta después de publicar esto que IOException era una clase base para algunos de estos, pero es el primero en la lista anterior. No importa para este código de todos modos, pero es un buen punto. –

0

Usted podría utilizar delegados, esto va a hacer lo que quiere:

EDIT: simplificado un poco

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; 
     } 
    } 
} 

Su particular, try/catch sería:

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 entienda que algunas de estas excepciones pueden no ser previsibles pero, cuando sea posible, intente implementar su propia lógica "preventiva". Las excepciones son caras, aunque en este caso probablemente no sea un factor decisivo.

Por ejemplo, use Directory.GetAccessControl (...) en lugar de basarse en una excepción de acceso no autorizado para ser lanzada.

+0

Las excepciones no son tan caras como para ser notorias para el ser humano. Tienes razón, en este caso definitivamente no es un factor decisivo, y lo más probable es que nunca suceda, prefiero no chocar si lo hace. –

2

Justo De forma complementaria:

En VB, se puede usar condicional manejo de excepciones:

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

Tal bloque Catch sólo se ingresan las excepciones especificadas - a diferencia de C#.

Quizás una versión futura de C# ofrezca una característica similar (después de todo, hay una instrucción específica de IL para ese código).

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

Cuestiones relacionadas