Leer el artículo de Eric Lippert sobre excepciones fue definitivamente una revelación sobre cómo debería abordar las excepciones, tanto como productor como como consumidor. Sin embargo, todavía estoy luchando por definir una directriz sobre cómo evitar lanzar excepciones molestas.
Específicamente:
- Suponga que tiene un método Save que puede fallar porque a) Alguien más modificó el registro antes que usted , ob) El valor que está intentando crear ya existe . Estas condiciones son de esperar y no excepcionales, por lo que en lugar de lanzar una excepción, decide crear una versión de prueba de su método, TrySave, que devuelve un valor booleano que indica si la operación de salvar se realizó correctamente. Pero si falla, ¿cómo sabrá el consumidor cuál fue el problema? ¿O sería mejor devolver una enumeración que indique el resultado, como Ok / RecordAlreadyModified / ValueAlreadyExists? Con integer. TryParse, este problema no existe, ya que solo hay una razón por la que el método puede fallar.
- ¿Es el ejemplo anterior realmente una situación irritante? ¿O lanzar una excepción en este caso sería la forma preferida? Sé que así se hace en la mayoría de las bibliotecas y marcos, incluido el marco Entity.
- ¿Cómo decide cuándo crear una versión de prueba de su método en lugar de proporcionar alguna forma de probar de antemano si el método funcionará o no? Actualmente estoy siguiendo estas pautas:
- Si existe la posibilidad de una condición de carrera, cree una versión de prueba. Esto evita la necesidad de que el consumidor atrape una excepción exógena. Por ejemplo, en el método Guardar descrito anteriormente.
- Si el método para probar la condición prácticamente haría todo lo que hace el método original, cree una versión de prueba. Por ejemplo, integer. TryParse ().
- En cualquier otro caso, cree un método para probar la condición.
exceptions
Micro
fuente
fuente
Respuestas:
Buena pregunta.
La primera pregunta que me viene a la mente es: si los datos ya están allí, ¿en qué sentido falló el guardado ? Seguro que suena como si me hubiera sucedido. Pero supongamos, en aras del argumento, que realmente tiene muchas razones diferentes por las que una operación puede fallar.
La segunda pregunta que me viene a la mente es: ¿ es accionable la información que desea devolver al usuario ? Es decir, ¿van a tomar alguna decisión basándose en esa información?
Cuando se enciende la luz de "revisar motor", abro el capó, verifico que hay un motor en mi auto que no está en llamas y lo llevo al garaje. Por supuesto, en el garaje tienen todo tipo de equipos de diagnóstico para propósitos especiales que les dicen por qué la luz de verificación del motor está encendida, pero desde mi perspectiva, el sistema de advertencia está bien diseñado. No me importa si el problema se debe a que el sensor de oxígeno está registrando un nivel anormal de oxígeno en la cámara de combustión, o porque el detector de velocidad de ralentí está desconectado, o lo que sea. Voy a tomar la misma acción, es decir, dejar que alguien más resuelva esto .
¿A la persona que llama le importa por qué falló el guardado? ¿Van a hacer algo al respecto, aparte de rendirse o volver a intentarlo?
Supongamos, en aras del argumento, que la persona que llama realmente va a tomar diferentes acciones dependiendo de la razón por la cual la operación falló.
La tercera pregunta que viene a la mente es: ¿ es excepcional el modo de falla ? Creo que puede ser confuso posible con excepcional . Pensaría en dos usuarios que intentan modificar el mismo registro al mismo tiempo que una situación excepcional pero posible, no una situación común.
Supongamos, por el bien del argumento, que no es excepcional.
La cuarta pregunta que viene a la mente es: ¿hay alguna manera de detectar de manera confiable la mala situación antes de tiempo?
Si la mala situación está en mi cubo "exógeno", entonces no. No hay forma de decir de manera confiable "¿otro usuario modificó este registro?" porque podrían modificarlo después de hacer la pregunta . La respuesta es obsoleta tan pronto como se produce.
La quinta pregunta que viene a la mente es: ¿hay alguna forma de diseñar la API para evitar la mala situación?
Por ejemplo, puede hacer que la operación "guardar" requiera dos pasos. Paso uno: adquiera un bloqueo en el registro que se está modificando. Esa operación tiene éxito o falla y, por lo tanto, puede devolver un booleano. La persona que llama puede tener una política sobre cómo lidiar con el fracaso: espere un momento e intente nuevamente, renunciar, lo que sea. Paso dos: una vez que se adquiere el bloqueo, guarde y suelte el bloqueo. Ahora el guardado siempre tiene éxito, por lo que no hay necesidad de preocuparse por ningún tipo de manejo de errores. Si el guardado falla, eso es realmente excepcional.
fuente
En su ejemplo, si la situación de ValueAlreadyExists puede verificarse fácilmente, debería verificarse y podría generarse una excepción antes de intentar Guardar, no creo que sea necesario un Try en esta situación. La condición de la carrera es más difícil de verificar con anticipación, por lo que, en este caso, envolver el Guardar en un intento es probablemente una muy buena idea.
En general, si hay una condición que creo que es muy probable (como NoDataReturned, DivideByZero, etc.) O es muy fácil de verificar (como una colección vacía o un valor NULL), trato de verificar antes de llegar al punto en el que tendría que atrapar una excepción. Admito que no siempre es fácil conocer estas condiciones con anticipación, a veces solo aparecen cuando el código está bajo pruebas rigurosas.
fuente
El
save()
método debe arrojar una excepción.La capa superior debe atrapar e informar al usuario , sin terminar el programa, a menos que sea un Unix como los programas de línea de comandos, en cuyo caso está bien terminar.
Los valores de retorno no son una buena forma de gestionar las excepciones.
fuente