2010-09-02 23 views
6

Siempre estoy luchando dónde colocar un bloque de captura de prueba. Por ejemplo, tengo una clase de base de datos con un método que acepta dos parámetros. FindObject (cadena donde, orden de cadena). Este método ejecuta una consulta sql con el lugar especificado y cadenas de orden.Dónde implementar try catch blocks?

En un curso que tienen una propiedad denominada IsUsed, esta propiedad tiene el siguiente aspecto:

public bool IsUsed 
{ 
get 
{ 
    ClassA a = new ClassA(); 
    Collection<ClassA> myCollection = a.FindObject("Id = 1",""); 

    if(..) // etc 
} 
} 

No importa si este enfoque es inteligente o no, lo único que quieren saber dónde colocar el intento atrapar si la ejecución de la consulta sql sale mal.

¿Dónde debo colocar la captura de intento para poder notificar al usuario que algo salió mal?

  1. ¿En el método FindObject?
  2. En la propiedad IsUsed?
  3. ¿Dónde llamo a la propiedad IsUsed?
  4. ¿Algún otro lugar? Pero donde
+0

Posible duplicado, aunque no es específico de C#: http://stackoverflow.com/questions/2119780/try-catch-blocks-inside-or-outside-of-functions-and-error-handing –

Respuesta

6

Solo atrape la excepción si puede hacer algo al respecto. De lo contrario, detecte excepciones en el nivel "más alto" de su aplicación y haga lo que sea necesario para manejarlo, incluida la terminación de su aplicación.

En las aplicaciones que tienen una IU, el nivel "más alto" suele ser controladores de eventos como el controlador de clic para un botón de búsqueda. Para los servicios en segundo plano, el nivel "más alto" es a menudo proclos de subprocesos o devoluciones de llamada de temporizador.

UI application       Background service 

| System Code |      | System Code | 
+----------------+      +----------------+ 
| Event Handler | <- try/catch here -> | Thread proc | 
|    |      |    | 
| ...   |      | ...   | 
|    |      |    | 
| Method   | <- throw here  -> | Method   | 

Si deja que la excepción se propaga de nuevo en el código del sistema que tendrá una excepción no controlada y que la aplicación se bloquee.

Manejar una excepción en una aplicación de interfaz de usuario a menudo implica mostrar un cuadro de mensaje. Algunas excepciones no son fatales y la operación puede reintentarse (por ejemplo, si falta un archivo o no se pudo realizar una consulta en la base de datos), pero otras excepciones son fatales y la única opción es finalizar la aplicación.

Un servicio en segundo plano registrará la excepción y quizás vuelva a intentar la operación.Si varios intentos fallan, el nivel de registro puede aumentar para llamar la atención de los operadores.

buena práctica cuando se trata de manejo de excepciones:

  • Si se coge una excepción y volver a lanzar propia excepción envolver la excepción original como el InnerException de la nueva excepción.
  • Si detecta una excepción, tal vez para hacer una limpieza, pero vuelva a lanzarla porque quiere que salte, siempre vuelva a lanzarla usando throw sin especificar ninguna excepción. Esto asegurará que la traza original de la pila no se destruya.
  • En la mayoría de los casos, solo debe tomar la clase base Exception en sus controladores de nivel superior.
  • Utilice finally bloques o mejor aún el patrón IDisposable para realizar una limpieza adecuada en su código.
  • Considere las excepciones como la "interfaz de usuario" para el desarrollador y formatee los mensajes de excepción en consecuencia. Si necesita una interfaz de usuario más pulida para el usuario menos técnico, probablemente debería ocultar las cosas más técnicas.
  • Trate de usar excepciones solo para situaciones excepcionales, como errores inesperados. No arroje excepciones para casos de error comunes. Verificar la existencia de una clave en un diccionario no debe arrojar una excepción, sino devolver el valor verdadero/falso.

Para responder a su pregunta específica, no creo que deba detectar ninguna excepción en su propiedad.

11

trato de seguir una regla muy sencilla: He definido un bloque try..catch si puedo manejar la excepción de una manera sensata. Si no hay nada significativo que pueda hacer sobre la excepción, la dejo burbujear al código de llamada.

Como nota al margen, evitaría la ejecución (potencialmente larga) de llamadas a la base de datos en un getter de propiedades. Por lo general, intento que las propiedades solo se establezcan u obtenga el valor de un campo de respaldo, permitiendo que otros métodos realicen búsquedas en la base de datos y demás. Eso hará que el código sea algo más predecible para la persona que escribe el código de llamada (es común que el acceso a la propiedad sea una acción barata, mientras que las llamadas a métodos a menudo pueden ser más caras).

+0

Gracias. Voy a utilizar un método. ¿Debo levantar un cuadro de mensaje en el método IsUsed para notificar al usuario que se ha producido una excepción? – Martijn

+1

@Martijn: sin saber más sobre su código, no puedo decir si es una buena idea mostrar un cuadro de mensaje o no, pero trato de evitar hacerlo a menos que el código esté muy cerca del usuario final. Si el código está en una biblioteca de clases, mostrar los cuadros de mensajes efectivamente hace que sea imposible usarlos en una aplicación web o similar. –

+1

También me gustaría agregar que este puede ser un buen método para permitir excepciones, pero a veces también debe usar el bloque 'finally' (especialmente con llamadas DB, donde se asegura de cerrar la conexión, si no se usa' usando 'bloque). En ese caso, solo vuelve a lanzar la excepción en la parte 'catch'. –

3

Debe colocar el bloque try-cacth en el lugar donde puede hacer algo significativo con la excepción detectada. Al igual que lo registra o se lo muestra al usuario. No atrape excepciones por el simple hecho de hacerlo.

+0

Pero mi punto es, ¿cuál es la mejor ubicación para mostrárselo al usuario? – Martijn

2

Bueno, debería ser donde se necesita el bloque try..catch, y donde se puede manejar. Supongo que esto estaría en el método FindObject aquí.

Por lo tanto, dentro del método FindObject, tome y maneje el SqlException, por ejemplo. Si necesita ser movido a un nivel más alto para un mejor manejo, entonces lanza la excepción, o deja que simplemente burbujee.

+0

Si el método falla, quiero notificar al usuario que se ha producido un error en la base de datos. ¿Cómo y dónde debo hacer esto? – Martijn

+0

@Martijn - Lo mejor que se puede hacer es alejar el código de la base de datos de la propiedad 'IsUsed' y tener un método para llamarlo. Entonces, podría manejar la excepción del método desde algún tipo de código de inicialización. Las llamadas a bases de datos en una propiedad pueden ponerse feas rápidamente. Más bien use el código de inicialización y establezca el valor de la propiedad. –

2

La única regla que se me ocurre es algo así como "en el nivel más bajo donde realmente se puede hacer algo útil con la información y no se está duplicando el código de manejo de excepciones".

Por ejemplo, si generalmente desea ver todas las excepciones relacionadas con el acceso a la base de datos de la misma manera, crearía una capa de abstracción de datos (hay muchas buenas razones para hacerlo) y colocaría el bloque try-catch allí .

El otro extremo sería una aplicación web en la que no esperas ninguna de esas excepciones, y Elmah detecta excepciones. En ese caso, no querrá utilizar un bloque try-catch en absoluto porque podría arruinar el registro.

2

Es fácil de responder: dónde puede tratarlo adecuadamente.

I.e, en el lugar dado, ¿puede tomar una decisión útil sobre la base de la captura? ¿Puedes devolver algo más? ¿Puedes decirle algo a alguien? ¿Puedes traducir el error a un error más útil en alguna otra capa de tu aplicación?

Si la respuesta es sí, entonces la atrapa allí. Si no es así para todos, no la atrape y deje que otra área lo haga.

FWIW, mi humilde opinión, su Get implementación también es demasiado complicado. Normalmente, creo que la gente no esperaría que una propiedad hiciera ese tipo de "trabajo". Mi opinión solo, sin embargo.

0

En lo que a mí respecta, me puso intento de captura bloques:

  • cuando un fin se necesita para Recursos de limpieza
  • cuando se necesita un flujo de operación específica en caso de fallo (reintentar por el usuario, iniciar sesión ...)

Por lo general, dejo que las excepciones salten al nivel superior, generalmente con algún controlador de eventos de control de winform. Solía ​​poner intente capturar aquí, utilizando un método de manejo de excepciones a nivel de aplicación.

De lo contrario, cuando estoy particularmente perezoso, engancho Application.ThreadException evento a un MessageBox.Show en el método de punto de entrada static void Main().

+2

Pero no necesitas un bloque catch para tener un bloque finally. – Polyfun

-1

Consulte, si recibe algún error, configure IsUsed como predeterminado. Algo como esto:

public bool IsUsed 
{ 
get 
{ 
    try{ 
    ClassA a = new ClassA(); 
    Collection<ClassA> myCollection = a.FindObject("Id = 1",""); 

    if(..) // etc 
    } 
    catch { return false;} 
} 
} 

Por lo tanto, siempre que haya algún error en FindObject, producirá falso.

+0

He pensado en este enfoque, pero no me gusta, ya que nunca sé si se ha producido una excepción en la base de datos. Esto hace que la depuración sea mucho más difícil. – Martijn

+0

@Martijn: sí, lo hace. Nunca trague una excepción a menos que realmente sea falsa o forme parte de una operación que no necesite tener éxito, como el registro de depuración (para algunos programas). – siride

+0

@Martijn Sí, por supuesto, puede lanzar la excepción, si es necesario. Pero, en general, si se produce una excepción dentro de las propiedades, simplemente devolvemos el valor predeterminado de la propiedad. Si su excepción es confidencial, puede registrar mensajes de excepción y mostrarle al usuario un mensaje agradable en lugar de detener todo el programa. – abhishek

1

general, como rule of thumb: El bloque try

contiene el código vigilado que puede causar la excepción

internamente en su caso, se puede manejar como:

FindObject() 
{ 
try{}catch{throw;//or throw new Exception("Some info"); 
} 

IsUser 
{ 
try{...a.FindObject("Id = 1",""); return true;} 
catch(Exception ex){Log(ex); return false;} 
} 

--EDIT--

Esto es en respuesta a @controlbreak comentario:

MSDN dice:

Usted puede lanzar una excepción explícita mediante la instrucción de tiro. También puede lanzar una excepción atrapada nuevamente usando la instrucción throw. Es una buena práctica de codificación agregar información a una excepción que se vuelve a lanzar para proporcionar más información cuando se depura.

+0

¿Por qué atrapar una excepción, no hacer nada con ella y tirarla de nuevo? – Martijn

+0

catch {throw;} no es útil. De todos modos, es bueno mencionar que catch {solo (no catch (Exception ...) se puede usar para atrapar excepciones no administradas. – Larry

+0

@Martinjn: Capturas la excepción y la vuelves a lanzar porque actualmente no tienes un controlador para no sabes qué quieres hacer con el error en particular, pero lo haces de nuevo lanzándolo para que alguien que esté "arriba de la capa" sepa qué hacer con el error. Por ejemplo: podrías Capture una excepción de "Servidor ocupado" y puede manejarla tratando de conectarse de nuevo, pero también detecta una excepción que dice "Servidor inactivo", ahora no sabe qué hacer ... así que lo hace burbujear. Probablemente el El método de llamada puede capturar y procesar el error correctamente. – ace

1

Depende de dónde desee manejar y para continuar el proceso.Supongamos que el código de llamada está a cargo de notificar al usuario, entonces un buen lugar es el código de llamada. El código de llamada tal vez necesite preguntar al usuario algo más después de haber notificado al usuario.

Considere también replantear la excepción que se produce. Si IsUsed reside en clase que no tiene nada que ver con las bases de datos y FindObject puede arrojar un SqlServerTimedOutException, aparecerá la pila de llamadas completa. Si esto es totalmente confuso schemantics captura la excepción y volver a lanzar esta manera:

 public bool IsUsed 
    { 
     get 
     {  
      ClassA a = new ClassA();  
      Collection<ClassA> myCollection; 

      try 
      { 
       myCollection = a.FindObject("Id = 1",""); 
      } 
      catch (SqlServerTimedOutException ex) 
      { 
       throw new MyApplicationException("Cannot evaluate if is in use.", ex); 
      } 

      if(..) // etc 
     } 
    } 

Pero no hacer uso excesivo de esto, ya que hace que el código más bien feo. Considerar cuidadosamente.

-1

No estoy de acuerdo con la mayoría de ustedes.

En primer lugar me gustaría recomendar este gran mensaje por Joel: http://www.joelonsoftware.com/items/2003/10/13.html

Teniendo esto en mente, usar bloques try-catch sólo como cerca de donde pueden salir mal posible.
Obviamente, a la IU le gustaría saberlo. Permita que tenga un valor de error que, pero no oculte la causa del problema real (detectando otros errores accidentales).

Si entiendo tu código correctamente, creo que habría utilizado el try-catch en la consulta real, y haré IsUsed nullabe para asegurarme de que entiendo que no se le asignó un valor.

+0

Suena bien en teoría, pero la realidad es que probablemente no pueda lidiar con la excepción en el momento en que se lanza. Quiero decir, tal vez puedas, y en ese caso, por supuesto atrapar y manejar. Pero requerir que cada nivel "maneje" cada error no solo crea un código feo, sino que probablemente crea un código incorrecto. Motivo: es posible que el código que genera la excepción no sepa lo suficiente para manejarlo, solo las capas superiores tendrán esa información (por ejemplo, para decidir si volver a intentarlo). – siride

0

Capture errores tan pronto como sea posible, y diseñe la llamada API para que pueda manejar el error sin usar excepciones. Las excepciones no detectadas son una amenaza para la estabilidad y, en cinco capas, es posible que no sepas si se lanzan excepciones en los casos extremos, a menos que las API inferiores expresen que pueden fallar.

A menudo puede diseñar la API para que tenga un estado de ejecución de retención de objeto de resultado en ella, que indicará al desarrollador consumidor que este método puede fallar y cómo falla.

Este enfoque a menudo le dará una forma significativa de manejar las excepciones en las partes de la API inferior, en lugar de simplemente dejarlas flotar hacia arriba al código de llamada.

Esto dice que hay idiomas en los que puede indicar que un método generará excepciones y qué excepciones arroja, pero escribió C# que no tiene esta funcionalidad.