2009-09-19 24 views
7

Estoy trabajando en una biblioteca diseñada para comunicarse (a través de comunicación serie RS232) con dispositivos externos. Estaba pensando en la estrategia de manejo de errores y las excepciones parecían ser correctas y la forma estándar de la industria de informar errores.Confundido sobre cuándo lanzar una excepción

Así que leí algunas pautas sobre excepciones. One indica con bastante claridad que yo no preocupación sobre impacto en el rendimiento:

No utilice códigos de error debido a la preocupación de que las excepciones podrían afectar negativamente al rendimiento.

Other me dijeron que no lanzar una excepción en casos normales:

No utilizar excepciones para los errores normales o esperados, o para el flujo normal de control.

No puedo trazar una línea clara entre normal/esperado y otros casos. Por ejemplo, en mi biblioteca, una operación puede fallar porque:

  1. No hay respuesta del dispositivo. (no hay cable conectado, el dispositivo no está encendido, tasa de baudios incorrecta)
  2. La solicitud de operación es rechazada por el dispositivo porque no pudo autenticar la solicitud.
  3. Se produjo un error en la comunicación. (Alguien tropezó con el cable, el dispositivo se apagó de repente).

se me ocurre todo lo anterior como era de esperar problemas, ya que pueden ocurrir en la práctica muy a menudo (de hecho muchos tarados de marketing me llaman para resolver el^problema^en mi software sólo para descubrir que aún no ha conecte el cable a su ordenador portátil) Por lo tanto, puede que no se den excepciones, ya que de lo contrario el programador de aplicaciones tendrá que atraparlas en muchos lugares (muchos bloqueos de captura tampoco son agradables).

Por otro lado, también tiendo a pensar que estos son todos los errores que de alguna manera tengo que informar al programador de aplicaciones, y la excepción parece ser la manera de hacerlo. Si no uso excepciones, tendré que informar estos problemas usando algún código de error o enumeraciones de error. (feo, lo sé).

¿Qué enfoque crees que debería tomar?

Respuesta

5

Está desarrollando una biblioteca , un componente que será utilizado por otras aplicaciones.

Por lo tanto, en los casos previstos que mencione, ciertamente usaré excepciones para comunicarme a la aplicación de llamada que algo anda mal. Debe definir una excepción personalizada para cada uno de estos escenarios y luego documentar claramente cuándo pueden ocurrir.

Esto permite que el código del cliente de la aplicación tome la decisión sobre la mejor manera de proceder. Solo la aplicación del cliente puede tomar esta decisión y las excepciones claramente documentadas pueden ayudar mucho.

Lo mejor de una excepción personalizada es que puede proporcionar varios datos significativos/útiles relacionados con el problema/excepción. Esta información también está muy bien encapsulada en un objeto. Compare esto con códigos de error y valores devueltos.

El rendimiento puede ser un problema, pero solo cuando la excepción se produce dentro de un circuito cerrado u otra situación de alta actividad. Para evitar esto, también podría aplicar un patrón utilizado por .NET Framework, es decir, proporcionar métodos Try ....() (p. Ej., TryParse()) que devuelvan boolean, indicando si una acción tuvo éxito o falló.

De cualquier forma me gustaría ir con excepciones personalizadas inicialmente, y luego hacer pruebas de rendimiento para ver realmente si hay ciertas partes de la biblioteca que pueden necesitar optimización.

+0

En cuanto al rendimiento, solo he visto una vez cuando el rendimiento entró en vigencia y hubo literalmente cientos de excepciones lanzadas y atrapadas una detrás de la otra. Tuvimos una operación que se podía hacer de dos maneras, una que a veces funcionaba pero arrojaba una excepción cuando no lo hacía, y la otra forma requería reflexión. Decidimos que la ruta de reflexión debería ser un retroceso si no podemos hacerlo sin reflexión. Resulta que se lanzaron tantas excepciones que era mucho más rápido usar siempre el método de reflexión (la diferencia era que el usuario esperaba 10 segundos frente a fracciones de segundo). – Davy8

1

Con RS232, a menos que tenga activado el handshaking de hardware (y la mayoría de las veces, las personas no), simplemente no verá más datos procedentes de la línea. No hay manera de que sepa si un dispositivo está conectado, aparte del hecho de que no se envía nada a la PC.

Clasificaría 1 y 3 juntos como RS232TimeoutError, y 2 como RS232AuthenticationError, probablemente.

En general, un TimeoutError indica que el dispositivo remoto se ha bloqueado o simplemente no está conectado. Un error de autenticación es como un ProtocolError, pero sutilmente diferente en que la comunicación es buena, pero el dispositivo remoto "simplemente dijo no" para crear una conexión con la PC.

Creo que establecer excepciones es bien justificado: nunca esperaría un error de tiempo de espera durante las operaciones normales, ni esperaría un error de autenticación.

+0

La biblioteca está destinada a ser utilizada para la comunicación con muchos dispositivos (32 dispositivos a la vez) y debe ser lo más rápida posible, ya que se utilizará en el entorno de producción. ¿Crees que elevar la excepción con demasiada frecuencia afectará el rendimiento? – Hemant

+1

Si tiene que ser tan rápido hasta el punto en que arrojar excepciones es un problema, debe escribir la biblioteca en C. Si genera una excepción de tiempo de espera, no vuelve inmediatamente y vuelve a hablar con el hardware y ve otra excepción: ignoras ese dispositivo hasta que te dicen explícitamente que vuelvas a él. –

+0

@Hemant: da la vuelta a la pregunta. Si los errores ocurren todo el tiempo, ¿cree que eso afectará el rendimiento? ¿Esperas que ocurran errores con frecuencia? ¿El rendimiento alcanzado debido al uso de una excepción será significativo en comparación con el golpe de rendimiento involucrado en la recuperación del error en el siguiente nivel de abstracción? –

1

Si arroja una excepción o no es una consecuencia del tipo de función.

  • Si la función devuelve una X y no puede determinar una X válida, ejecute una excepción.
  • Si la función es una acción (por ejemplo, Connect) y no puede completar la acción, haga una excepción.
  • Si la función es de la variedad TryX, no arroje una excepción.

Así que supongo que estoy diciendo que deberías sacar el problema de "¿Debo lanzar una excepción?" a "¿Qué métodos querrán las personas que llaman a mi biblioteca?" con la advertencia de que las excepciones que arroje deben ser obvias según los métodos que proporcione.

3

me gustaría utilizar excepciones, en el siguiente enfoque (inspiradas en el diseño por contrato)

  • Siempre que sea posible, proveer funciones inspección booleanas que le dice si una operación puede decir con seguridad aplica
  • para la operación dada, considere una función de inspección como condición previa: si se cumple, la operación se puede realizar de forma segura, si no, lanza una excepción.

De esta forma, si el usuario de API puede codificar su lógica de teclas utilizando estructuras if-then-else.

Si surgen situaciones inesperadas (debido a problemas de sincronización sutiles, por ejemplo) se lanzará una excepción: el desarrollador puede detectar esta excepción y tratar con ella.Pero tenga en cuenta: esto no tiene que ser en el lugar donde se invocó el método: puede ser más alto/más temprano en la pila de llamadas, en un lugar central donde se manejan todas las extrañas excepciones

He hecho algo de trabajo en el análisis automatizado del código de manejo de errores en líneas de millones de millones de programas C. Estos se basaron en estándares de codificación que requieren una inspección escrita a mano y la propagación de códigos de error. Resulta que a los desarrolladores no les gusta escribir dicho código, lo olvidan y fácilmente cometen errores. De hecho, encontramos 2 desviaciones de los estándares de codificación (2 fallas, podría decirse) por 1000 líneas de código C.

En resumen: (1) que haría uso de los inspectores booleanas (2) las excepciones pueden capturarse en lugares más altos en la pila de llamadas; (3) confiar en los códigos de error no es seguro en la práctica.

+1

¡Diseñe por contrato para la victoria! – akuhn

1

No utilizar excepciones para los errores normales o esperados , o para el flujo normal de control.

Dentro de las implementaciones de su método, evite causar una excepción deliberadamente para cambiar el flujo de ejecución, manejar la lógica especial, casos especiales o manejar errores normales o esperados. Por ejemplo, el manejo de excepciones debe eliminarse en la siguiente función. (Está manejando errores normales o esperados, pero como la nota dice Convert.ToString realmente no va a fallar). Hay un pequeño golpe de rendimiento debido al tiempo necesario para "configurar" el manejo de excepciones dentro del método. No es un golpe significativo, sin embargo, si está llamando a esta función en un bucle, entonces puede volverse significativa. Si este método se encuentra en una biblioteca, permita que todas las excepciones pasen al usuario de la biblioteca. (Excepciones personalizados son diferentes, véase la respuesta de Ash.)

Public Function Nz(ByVal value As String, ByVal valueIfNothing As String) As String 
    Try 
     Dim sValue As String = System.Convert.ToString(value) 'using Convert.ToString on purpose 
     If IsNothing(sValue) Then 
      Return valueIfNothing 
     Else 
      Return sValue 
     End If 
    Catch ex As Exception 
     'Convert.ToString handles exceptions, but just in case... 
     Debug.Fail("Nz() failed. Convert.ToString threw exception.") 
     Return String.Empty 
    End Try 
End Function 

Aquí es una aplicación "mejor" del método:

Public Function Nz(ByVal value As String, ByVal valueIfNothing As String) As String 
    Dim sResult As String = String.Empty 
    Dim sValue As String = System.Convert.ToString(value) 'using Convert.ToString on purpose 
    If IsNothing(sValue) Then 
     sResult = valueIfNothing 
    Else 
     sResult = sValue 
    End If 
    Return sResult 
End Function 
1

No utilice códigos de error debido a preocupación de que las excepciones podrían afectar el rendimiento negativamente.

Evite diseñar todo como una función que devuelve verdadero/falso con parámetros de "salida" solo para evitar problemas de rendimiento "imaginarios" con el uso de excepciones.

0

La elección no tiene que ser entre lanzar una excepción o devolver un código de error. Intente devolver un objeto de excepción en una condición excepcional. El golpe de rendimiento no está en crear una excepción, sino en lanzarla. La persona que llama puede verificar el valor de retorno "nulo". La excepción devuelta podría ser un retorno de función, o uno de varios parámetros de "salida".

2

Las excepciones se diseñaron exactamente para eso: circunstancias excepcionales. Intento pensarlo de esta manera: si supone que todo lo que necesita funciona normalmente durante la mayor parte del tiempo, y el método X falla, entonces es una circunstancia excepcional porque normalmente no ocurre y debe definir excepciones. para atrapar la situación

En su situación, usted supone que el dispositivo está en funcionamiento. Por lo tanto, circunstancias excepcionales en este caso son que el dispositivo no acepta una conexión, rechaza una conexión, no acepta los datos que usted envía o no recibe los datos que debería enviar, etc. Si sus dispositivos se apagan rutinariamente varias veces al día, entonces esperas que se apaguen, así que usa códigos de retorno o un "bool IsDeviceOn();" método para verificarlo antes de conectarse.

Si se espera que suceda en circunstancias normales, como consultar un dispositivo por sus capacidades pero el que desea no está disponible, utilice códigos de retorno o un método bool, p. "bool DoesDeviceHaveThisCapability();" No atrape una excepción cuando no lo haga.

Otro ejemplo (para aplicaciones de GUI) es la entrada del usuario. No use excepciones para eso, porque espera que un usuario ingrese algo incorrectamente, no somos perfectos.

He tenido problemas de rendimiento masivo debido al uso de excepciones cuando no eran circunstancias realmente excepcionales. Un ejemplo fue cuando procesaba un archivo de datos de 2 GB 2-3 veces al día. Algunas líneas tienen precios válidos en el formato 0.00. Algunos no lo hicieron. Usé una FormatException para capturar los que no.

Dos años más tarde, cuando tuve la oportunidad de obtener un analizador de rendimiento, esa línea en particular que captó la excepción fue responsable del 80% del tiempo utilizado para analizar el archivo. Lo cambié para usar el método TryParse() de int en su lugar, y obtuve un aumento de velocidad masivo.

Para sus ejemplos, probablemente utilizar:

  1. No hay respuesta del dispositivo - excepción si los dispositivos deben estar en 24/7, código de retorno si se alimenta de forma rutinaria fuera
  2. operación no autorizada - excepción
  3. Comms failed - excepción
+0

Terminé haciendo exactamente lo que sugeriste. Gracias. – Hemant

0

Si un método no puede realizar su función principal de una manera que una persona que llama estará preparada para tratar, la función generalmente debe indicar tal fai atraer a través del valor de retorno (o, raramente, escribiendo a una variable que se pasa por referencia). Si una función falla de una manera que la persona que llama probablemente no esté preparada para enfrentar, es mejor lanzar una excepción. Si algunas personas que llaman estarían preparadas para tratar una falla, y otras personas que llaman no lo harían, entonces es mejor usar el patrón try/do. Básicamente, uno proporciona dos métodos: DoSomething y TryDoSomething. DoSomething promete que tendrá éxito o lanzará una excepción. TryDoSomething promete que no arrojará una excepción a menos que ocurra algo verdaderamente inesperado; indicará fallas "esperadas" a través del valor de retorno.

Cuestiones relacionadas