¿Cuándo lanzar una excepción?

435

Tengo excepciones creadas para cada condición que mi aplicación no espera. UserNameNotValidException, PasswordNotCorrectExceptionEtc.

Sin embargo, me dijeron que no debía crear excepciones para esas condiciones. En mi UML esas SON excepciones al flujo principal, entonces ¿por qué no debería ser una excepción?

¿Alguna orientación o mejores prácticas para crear excepciones?

Kwan Cheng
fuente
59
Vuelva a abrir, esta es una pregunta muy sensata y válida. Cualquier pregunta implica una cierta cantidad de opinión, pero en este caso sospecho que es una cuestión de "mejores prácticas".
Tim Long
19
+1 para reabrir. Como muchos otros temas interesantes "depende" y es muy útil analizar las compensaciones al tomar decisiones. El hecho de que las personas confundan opiniones con hechos en las respuestas no lo niega. Tamizar el barro es un ejercicio que debe dejarse al lector.
aron
12
También estoy de acuerdo en que esta pregunta debe reabrirse ya que está relacionada con las mejores prácticas. Por cierto, las mejores prácticas son siempre opiniones que pueden ayudar a otros.
Ajay Sharma
66
Microsoft dice: "No devuelva códigos de error. Las excepciones son el medio principal para informar errores en los marcos". y "... Si un miembro no puede hacer con éxito lo que está diseñado para hacer, eso debería considerarse un error de ejecución y se debería lanzar una excepción". msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75
44
Estas pueden ser una excepción totalmente sensata, solo depende de qué métodos las arrojen. Un método llamado IsCredentialsValid(username,password)no debe arrojar una excepción si el nombre de usuario o la contraseña no son válidos, pero devuelve falso. Pero supongamos que un método que lee datos de la base de datos podría arrojar legítimamente tal excepción, si falla la autenticación. En resumen: debe lanzar una excepción si un método no puede realizar la tarea que se supone que debe hacer.
JacquesB

Respuestas:

629

Mi pauta personal es: se produce una excepción cuando se descubre que una suposición fundamental del bloque de código actual es falsa.

Ejemplo 1: digamos que tengo una función que se supone que examina una clase arbitraria y devuelve verdadero si esa clase hereda de la Lista <>. Esta función hace la pregunta, "¿Es este objeto un descendiente de la Lista?" Esta función nunca debería arrojar una excepción, porque no hay áreas grises en su operación: cada clase hereda o no de la Lista <>, por lo que la respuesta es siempre "sí" o "no".

Ejemplo 2: digamos que tengo otra función que examina una Lista <> y devuelve verdadero si su longitud es mayor que 50, y falso si la longitud es menor. Esta función hace la pregunta, "¿Esta lista tiene más de 50 elementos?" Pero esta pregunta supone: supone que el objeto que se le da es una lista. Si le doy un NULL, entonces esa suposición es falsa. En ese caso, si la función devuelve ya sea verdadera o falsa, entonces es romper sus propias reglas. La función no puede devolver nada y afirmar que respondió la pregunta correctamente. Por lo tanto, no regresa, arroja una excepción.

Esto es comparable a la falacia lógica de la "pregunta cargada" . Cada función hace una pregunta. Si la entrada que se da hace que esa pregunta sea una falacia, entonces arroje una excepción. Esta línea es más difícil de dibujar con funciones que devuelven vacío, pero la conclusión es: si se violan los supuestos de la función sobre sus entradas, debería arrojar una excepción en lugar de regresar normalmente.

El otro lado de esta ecuación es: si encuentra que sus funciones arrojan excepciones con frecuencia, entonces probablemente necesite refinar sus suposiciones.

The Digital Gabeg
fuente
15
¡Exactamente! ¡Se produce una excepción cuando y solo cuando se rompen las condiciones previas de la función (supuestos sobre argumentos) !
Lightman
11
En lingüística, esto a veces se llama falla de presuposición . El ejemplo clásico se debe a Bertrand Russell: "¿Es calvo el Rey de Francia?" No se puede responder sí o no, (resp. "El Rey de Francia es calvo" no es ni verdadero ni falso), porque contiene una presuposición falsa , a saber, que hay un Rey de Francia. La falla de la preposición se ve a menudo con descripciones definidas, y eso es común cuando se programa. Por ejemplo, "El encabezado de una lista" tiene un error de presuposición cuando una lista está vacía, y luego es apropiado lanzar una excepción.
Mohan
¡Esta es posiblemente la mejor explicación!
gaurav
Gracias. Entonces. Mucho. "Es el rey de Francia calvo". He escuchado esto antes cuando investigaba la jungla de meinong ... :) Gracias. @Mohan
ErlichBachman
285

Porque son cosas que sucederán normalmente. Las excepciones no son mecanismos de control de flujo. Los usuarios suelen tener contraseñas incorrectas, no es un caso excepcional. Las excepciones deben ser algo realmente raro, UserHasDiedAtKeyboardescriba situaciones.

blowdart
fuente
3
Hmm, no Las excepciones se pueden usar como mecanismos de control de flujo si no se requiere un rendimiento máximo, lo cual es cierto para la mayoría de las aplicaciones web. Python usa la excepción 'StopIteration' para terminar los iteradores, y funciona muy bien. El costo no es nada en comparación con IO, etc.
Seun Osewa
99
+1 excelente respuesta. Estoy tan frustrado por los desarrolladores que trabajan en API que tengo que consumir y arrojar Excepciones por cada pequeña cosa. Muy pocos casos realmente requieren excepciones. Si tiene 25 tipos diferentes de excepciones definidas, eche un vistazo a su diseño nuevamente, es posible que lo esté haciendo mal.
7wp
1
¿Debería haber una excepción cuando el usuario intenta una acción ilegal que no le está permitido manipular el código de la página web, por ejemplo, para eliminar las publicaciones de otra persona aquí en StackOverflow?
Rajat Gupta
30
Las excepciones SON mecanismos de control de flujo. Puedes tirarlos. Puedes atraparlos. Moviste el control a otra pieza de código. Eso es control de flujo. La única razón por la cual los idiomas tienen excepciones es para que puedas escribir código directo sin preguntar "¿acaso eso falló?" después de todo lo que haces Haskell, por ejemplo, no tiene excepciones porque las mónadas y las anotaciones pueden automatizar la comprobación de errores por usted.
Jesse
2
Las excepciones son más que mecanismos de control de flujo. Brindan al cliente (método) información útil sobre resultados excepcionales que deben conocer y manejar. Es decir, si se usan correctamente, las excepciones hacen que las API sean más robustas
idelvall
67

Mis pequeñas pautas están fuertemente influenciadas por el gran libro "Código completo":

  • Use excepciones para notificar sobre cosas que no deben ignorarse.
  • No use excepciones si el error puede manejarse localmente
  • Asegúrese de que las excepciones estén en el mismo nivel de abstracción que el resto de su rutina.
  • Las excepciones deben reservarse para lo que es realmente excepcional .
Commander Keen
fuente
35

NO es una excepción si el nombre de usuario no es válido o la contraseña no es correcta. Esas son cosas que debe esperar en el flujo normal de operación. Las excepciones son cosas que no son parte de la operación normal del programa y son bastante raras.

EDITAR: No me gusta usar excepciones porque no se puede saber si un método arroja una excepción simplemente mirando la llamada. Es por eso que las excepciones solo deben usarse si no puede manejar la situación de manera decente (piense "sin memoria" o "la computadora está en llamas").

EricSchaefer
fuente
"No me gusta usar excepciones porque no se puede saber si un método arroja una excepción con solo mirar la llamada". Por eso hay excepciones marcadas para los idiomas que las admiten.
Newtopian
1
Las excepciones marcadas tienen su propio conjunto de problemas. Prefiero utilizar excepciones de "circunstancias excepcionales", no para cosas que son parte del flujo de trabajo normal.
EricSchaefer
2
En respuesta a tu edición. Siempre pongo en mis documentos xml al final de la sección de resumen las excepciones que arroja la función para que pueda ver esa información en intellisense.
Matthew Vines
1
¿Debería haber una excepción cuando el usuario intenta una acción ilegal que no le está permitido manipular el código de la página web, por ejemplo, para eliminar las publicaciones de otra persona aquí en StackOverflow?
Rajat Gupta
1
Parece que está hablando de errores de manejo (nuestro de memoria, computadora en llamas) frente a excepciones de código de manejo (registro faltante, tipo de entrada no válida, etc.). Creo que hay una clara diferencia entre los dos.
Chuck Burgess
28

Una regla general es usar excepciones en el caso de algo que normalmente no podría predecir. Ejemplos son la conectividad de la base de datos, el archivo faltante en el disco, etc. Para los escenarios que puede predecir, es decir, los usuarios que intentan iniciar sesión con una contraseña incorrecta, deberían usar funciones que devuelvan booleanos y sepan cómo manejar la situación con gracia. No desea finalizar abruptamente la ejecución lanzando una excepción solo porque alguien escribió mal su contraseña.

japollock
fuente
66
no necesita detener la ejecución del programa en una excepción ... arroje la excepción, la persona que llama luego capta la excepción y debe manejarla, si es posible, registrar y error y seguir adelante. Es una 'mala forma' seguir lanzando excepciones en la pila de llamadas: atraparlo donde ocurre y lidiar con eso allí
Krakkos
2
pero ¿por qué incluso tirarlos si puedes manejarlo directamente? si la contraseña es incorrecta o algo está mal, solo dejo que un if devuelva un falso y dé un error
My1
" archivo perdido en el disco " La mayoría de los frameworks de idiomas, por ejemplo .NET Framework, también proporcionan API para verificar la existencia de archivos. ¡Por qué no usarlos antes de acceder al archivo directamente!
user1451111
23

Otros proponen que no se usen excepciones porque el inicio de sesión incorrecto se espera en un flujo normal si el usuario escribe incorrectamente. No estoy de acuerdo y no entiendo el razonamiento. Compárelo con abrir un archivo ... si el archivo no existe o no está disponible por alguna razón, el marco generará una excepción. Usar la lógica anterior fue un error de Microsoft. Deberían haber devuelto un código de error. Lo mismo para el análisis, webrequests, etc., etc.

No considero que un inicio de sesión incorrecto forme parte de un flujo normal, es excepcional. Normalmente, el usuario escribe la contraseña correcta y el archivo existe. Los casos excepcionales son excepcionales y está perfectamente bien usar excepciones para ellos. Complicar su código al propagar valores de retorno a través de n niveles en la pila es un desperdicio de energía y dará como resultado un código desordenado. Haz lo más simple que pueda funcionar. No optimice prematuramente mediante el uso de códigos de error, raramente ocurren cosas excepcionales por definición, y las excepciones no cuestan nada a menos que las arroje.

Bjorn Reppen
fuente
Excepto que puede verificar que el archivo existe antes de llamar a abrir (dependiendo de su marco, por supuesto). Entonces, esa facilidad existe y, por lo tanto, si el archivo desaparece entre la verificación y usted intenta abrirlo, entonces eso es una excepción.
blowdart
77
El archivo existente no significa que el usuario pueda escribir en el archivo, por ejemplo. Verificar cada posible problema es realmente tedioso y propenso a errores. + estás duplicando el código (DRY).
Bjorn Reppen
Un punto con la excepción de contraseña inválida es que cualquier lentitud en comparación con la solución del código de retorno no será perceptible para el usuario humano que ingresa una contraseña.
paperhorse
77
"Complicar su código al propagar valores de retorno a través de n niveles en la pila es una pérdida de energía y dará como resultado un código desordenado". Eso, para mí, es una razón muy fuerte para usar excepciones. Un buen código generalmente se compone de pequeñas funciones. No desea pasar ese código de error una y otra vez de una función pequeña a otra.
beluchin
Creo que la confusión resulta de la suposición de que un resultado predecible de un loginmétodo de tipo sería que una contraseña puede ser incorrecta, de hecho puede usarse para determinar esto, y no querría tener una excepción en este caso; mientras que en un file openescenario tipo, que tiene un resultado particular deseado, si el sistema no puede entregar el resultado debido a parámetros de entrada incorrectos o algún factor externo, ese es un uso perfectamente lógico de una excepción.
theMayer
17

Las excepciones son un efecto algo costoso, si, por ejemplo, tiene un usuario que proporciona una contraseña no válida, generalmente es una mejor idea devolver un indicador de falla o algún otro indicador de que no es válido.

Esto se debe a la forma en que se manejan las excepciones, la entrada incorrecta verdadera y los elementos de parada críticos únicos deben ser excepciones, pero no información de inicio de sesión fallida.

Mitchel Sellers
fuente
14

Creo que solo deberías lanzar una excepción cuando no hay nada que puedas hacer para salir de tu estado actual. Por ejemplo, si está asignando memoria y no hay ninguna para asignar. En los casos que menciona, puede recuperarse claramente de esos estados y puede devolver un código de error a su interlocutor en consecuencia.


Verá muchos consejos, incluso en las respuestas a esta pregunta, de que debe lanzar excepciones solo en circunstancias "excepcionales". Eso parece superficialmente razonable, pero es un consejo erróneo, porque reemplaza una pregunta ("cuándo debo lanzar una excepción") con otra pregunta subjetiva ("qué es excepcional"). En cambio, siga los consejos de Herb Sutter (para C ++, disponible en el artículo del Dr. Dobbs Cuándo y cómo usar excepciones , y también en su libro con Andrei Alexandrescu, C ++ Coding Standards ): arroje una excepción si, y solo si

  • no se cumple una condición previa (que normalmente hace que uno de los siguientes sea imposible) o
  • la alternativa no cumpliría una condición posterior o
  • la alternativa no lograría mantener una invariante.

¿Por qué es esto mejor? ¿No reemplaza la pregunta con varias preguntas sobre condiciones previas, condiciones posteriores e invariantes? Esto es mejor por varias razones relacionadas.

  • Condiciones previas, condiciones posteriores e invariantes son características de diseño de nuestro programa (su API interna), mientras que la decisión throwes un detalle de implementación. Nos obliga a tener en cuenta que debemos considerar el diseño y su implementación por separado, y nuestro trabajo al implementar un método es producir algo que satisfaga las restricciones de diseño.
  • Nos obliga a pensar en términos de precondiciones, postcondiciones e invariantes, que son las únicas suposiciones que deben hacer los llamadores de nuestro método, y se expresan con precisión, permitiendo un acoplamiento flexible entre los componentes de nuestro programa.
  • Ese acoplamiento flojo nos permite refactorizar la implementación, si es necesario.
  • Las condiciones posteriores y las invariantes son comprobables; da como resultado un código que puede probarse fácilmente en la unidad, porque las condiciones posteriores son predicados que nuestro código de prueba unitaria puede verificar (afirmar).
  • Pensar en términos de postcondiciones produce naturalmente un diseño que tiene éxito como postcondición , que es el estilo natural para usar excepciones. La ruta de ejecución normal ("feliz") de su programa se presenta linealmente, con todo el código de manejo de errores trasladado a las catchcláusulas.
Raedwald
fuente
10

Yo diría que no hay reglas estrictas sobre cuándo usar excepciones. Sin embargo, hay buenas razones para usarlas o no:

Razones para usar excepciones:

  • El flujo de código para el caso común es más claro
  • Puede devolver información de error compleja como un objeto (aunque esto también se puede lograr utilizando el parámetro de error "out" pasado por referencia)
  • Los lenguajes generalmente brindan alguna facilidad para administrar la limpieza ordenada en caso de excepción (intente / finalmente en Java, use en C #, RAII en C ++)
  • En el caso de que no se produzca una excepción, la ejecución a veces puede ser más rápida que verificar los códigos de retorno
  • En Java, las excepciones marcadas deben declararse o capturarse (aunque esto puede ser una razón en contra)

Razones para no usar excepciones:

  • A veces es excesivo si el manejo de errores es simple
  • Si las excepciones no se documentan o declaran, se pueden detectar mediante el código de llamada, que puede ser peor que si el código de llamada simplemente ignorara un código de retorno (la salida de la aplicación frente a una falla silenciosa, lo que es peor, puede depender del escenario)
  • En C ++, el código que usa excepciones debe ser seguro para excepciones (incluso si no las arroja ni las atrapa, pero llama a una función de lanzamiento indirectamente)
  • En C ++, es difícil saber cuándo puede lanzar una función, por lo tanto, debe ser paranoico sobre la seguridad de las excepciones si las usa
  • Lanzar y atrapar excepciones es generalmente significativamente más costoso en comparación con verificar una bandera de retorno

En general, estaría más inclinado a usar excepciones en Java que en C ++ o C #, porque considero que una excepción, declarada o no, es fundamentalmente parte de la interfaz formal de una función, ya que cambiar su garantía de excepción puede romper el código de llamada La mayor ventaja de usarlos en Java IMO es que sabe que su interlocutor DEBE manejar la excepción, y esto mejora las posibilidades de un comportamiento correcto.

Debido a esto, en cualquier idioma, siempre derivaría todas las excepciones en una capa de código o API de una clase común, por lo que el código de llamada siempre puede garantizar la captura de todas las excepciones. También consideraría que es malo lanzar clases de excepción que son específicas de la implementación, al escribir una API o biblioteca (es decir, ajustar las excepciones de las capas inferiores para que la excepción que recibe la persona que llama sea comprensible en el contexto de su interfaz).

Tenga en cuenta que Java hace la distinción entre excepciones generales y de tiempo de ejecución en que este último no necesita ser declarado. Solo usaría las clases de excepción de tiempo de ejecución cuando sepa que el error es el resultado de un error en el programa.

Robert
fuente
5

Las clases de excepción son como las clases "normales". Se crea una nueva clase cuando "es" un tipo diferente de objeto, con diferentes campos y diferentes operaciones.

Como regla general, debe intentar equilibrar el número de excepciones y la granularidad de las excepciones. Si su método arroja más de 4-5 excepciones diferentes, probablemente pueda fusionar algunas de ellas en excepciones más "generales" (por ejemplo, en su caso "AuthenticationFailedException"), y usar el mensaje de excepción para detallar qué salió mal. A menos que su código maneje cada uno de ellos de manera diferente, no necesita crear muchas clases de excepción. Y si lo hace, puede que simplemente devuelva una enumeración con el error que ocurrió. Es un poco más limpio de esta manera.

Shachar
fuente
5

Si el código se ejecuta dentro de un bucle que probablemente causará una excepción una y otra vez, entonces lanzar excepciones no es algo bueno, porque son bastante lentas para la gran N. Pero no hay nada de malo en lanzar excepciones personalizadas si el rendimiento no es un problema. Solo asegúrese de tener una excepción base que todos hereden, llamada BaseException o algo así. BaseException hereda System.Exception, pero todas sus excepciones heredan BaseException. Incluso puede tener un árbol de tipos de Excepción para agrupar tipos similares, pero esto puede ser excesivo o no.

Entonces, la respuesta corta es que si no causa una penalización de rendimiento significativa (que no debería a menos que esté lanzando muchas excepciones), entonces continúe.

Charles Graham
fuente
Realmente me gustó tu comentario sobre las excepciones dentro de un bucle y pensé en probarlo yo mismo. Escribí un programa de muestra ejecutando int.MaxValuetiempos de bucle y generando la excepción 'dividir por cero'. La versión IF / ELSE, en la que estaba comprobando si el dividendo no es cero antes de la operación de división , se completó en 6082 ms y 15407722, mientras que la versión TRY / CATCH, en la que estaba generando la excepción y capturando la excepción , se completó en 28174385 ms y 71371326155 ticks: la friolera de 4632 veces más que la versión if / else.
user1451111
3

Estoy de acuerdo con japollock allá arriba: presente una aceptación cuando no esté seguro del resultado de una operación. Llamadas a API, acceso a sistemas de archivos, llamadas a bases de datos, etc. Cada vez que se mueva más allá de los "límites" de sus lenguajes de programación.

Me gustaría agregar, siéntase libre de lanzar una excepción estándar. A menos que vaya a hacer algo "diferente" (ignorar, enviar un correo electrónico, iniciar sesión, mostrar esa imagen de ballena de Twitter, etc.), no se moleste con excepciones personalizadas.

dclaysmith
fuente
3

La regla general para lanzar excepciones es bastante simple. lo hace cuando su código ha entrado en un estado NO RECUPERABLE NO VÁLIDO. Si los datos se ven comprometidos o no puede cancelar el procesamiento que se produjo hasta el momento, debe finalizarlos. de hecho, ¿qué más puedes hacer? su lógica de procesamiento finalmente fallará en otro lugar. Si puede recuperarse de alguna manera, hágalo y no arroje una excepción.

en su caso particular, si se vio obligado a hacer algo tonto como aceptar el retiro de dinero y solo luego verificar el usuario / contraseña, debe finalizar el proceso lanzando una excepción para notificar que algo malo ha sucedido y evitar daños mayores.

goran
fuente
2

En general, desea lanzar una excepción por cualquier cosa que pueda suceder en su aplicación que sea "Excepcional"

En su ejemplo, parece que ambas excepciones las está llamando a través de una contraseña / validación de nombre de usuario. En ese caso, se puede argumentar que no es realmente excepcional que alguien escriba mal un nombre de usuario / contraseña.

Son "excepciones" al flujo principal de su UML pero son más "ramas" en el procesamiento.

Si intentó acceder a su archivo o base de datos passwd y no pudo, ese sería un caso excepcional y garantizaría lanzar una excepción.

Gord
fuente
" Si intentaste acceder a tu archivo o base de datos passwd y no pudiste, sería un caso excepcional y justificaría una excepción ". ¡Por qué no usarlos antes de acceder al archivo directamente!
user1451111
2

En primer lugar, si los usuarios de su API no están interesados ​​en fallas específicas y específicas, entonces tener excepciones específicas para ellos no tiene ningún valor.

Como a menudo no es posible saber qué puede ser útil para sus usuarios, un mejor enfoque es tener las excepciones específicas, pero asegurarse de que hereden de una clase común (por ejemplo, std :: exception o sus derivados en C ++). Eso le permite a su cliente detectar excepciones específicas si así lo desea, o la excepción más general si no le importa.

Jason Etheridge
fuente
2

Las excepciones están destinadas a eventos que son comportamientos anormales, errores, fallas, etc. El comportamiento funcional, el error del usuario, etc., deben ser manejados por la lógica del programa. Dado que una cuenta o contraseña incorrecta es una parte esperada del flujo lógico en una rutina de inicio de sesión, debería ser capaz de manejar esas situaciones sin excepciones.

Joe Skora
fuente
2

Tengo problemas filosóficos con el uso de excepciones. Básicamente, usted espera que ocurra un escenario específico, pero en lugar de manejarlo explícitamente, está empujando el problema para que se maneje "en otro lugar". Y dónde está ese "otro lugar" puede ser una incógnita.

Dan
fuente
2

Yo diría que, en general, todo fundamentalismo lleva al infierno.

Ciertamente, no querrás terminar con un flujo impulsado por excepciones, pero evitar las excepciones por completo también es una mala idea. Tienes que encontrar un equilibrio entre ambos enfoques. Lo que no haría es crear un tipo de excepción para cada situación excepcional. Eso no es productivo.

Lo que generalmente prefiero es crear dos tipos básicos de excepciones que se utilizan en todo el sistema: LogicalException y TechnicalException . Estos pueden distinguirse aún más por subtipos si es necesario, pero generalmente no es necesario.

La excepción técnica denota la excepción realmente inesperada, como que el servidor de la base de datos está inactivo, la conexión al servicio web arrojó la IOException, etc.

Por otro lado, las excepciones lógicas se utilizan para propagar la situación errónea menos grave a las capas superiores (generalmente, algún resultado de validación).

Tenga en cuenta que incluso la excepción lógica no está destinada a ser utilizada regularmente para controlar el flujo del programa, sino para resaltar la situación en la que el flujo realmente debería terminar. Cuando se usa en Java, ambos tipos de excepción son subclases de RuntimeException y el manejo de errores está altamente orientado a aspectos.

Por lo tanto, en el ejemplo de inicio de sesión, sería aconsejable crear algo como AuthenticationException y distinguir las situaciones concretas mediante valores de enumeración como UsernameNotExisting , PasswordMismatch , etc. Entonces no terminará teniendo una gran jerarquía de excepciones y puede mantener los bloques de captura en un nivel mantenible . También puede emplear fácilmente algún mecanismo genérico de manejo de excepciones, ya que tiene las excepciones categorizadas y sabe muy bien qué propagar al usuario y cómo.

Nuestro uso típico es lanzar la excepción lógica durante la llamada al servicio web cuando la entrada del usuario no es válida. La excepción se ordena al detalle SOAPFault y luego se desarma a la excepción nuevamente en el cliente, lo que resulta en mostrar el error de validación en un determinado campo de entrada de página web, ya que la excepción tiene una asignación adecuada a ese campo.

Ciertamente, esta no es la única situación: no es necesario acceder al servicio web para generar la excepción. Usted es libre de hacerlo en cualquier situación excepcional (como en el caso de que necesite fallar rápidamente), todo es a su discreción.

Petr Macek
fuente
2

para mí Se debe lanzar una excepción cuando falla una regla técnica o comercial requerida. por ejemplo, si una entidad de automóvil está asociada con un conjunto de 4 llantas ... si una llanta o más son nulas ... una excepción debe activarse "NotEnoughTiresException", porque puede detectarse en un nivel diferente del sistema y tener un impacto significativo significado a través de la tala. además si solo tratamos de controlar el flujo del nulo y evitar la instanciación del automóvil. Es posible que nunca encontremos la fuente del problema, porque no se supone que la llanta sea nula en primer lugar.

Genjuro
fuente
1

La razón principal para evitar lanzar una excepción es que hay muchos gastos generales involucrados en lanzar una excepción.

Una cosa que establece el artículo a continuación es que una excepción es para condiciones y errores excepcionales.

Un nombre de usuario incorrecto no es necesariamente un error del programa, sino un error del usuario ...

Aquí hay un punto de partida decente para excepciones dentro de .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

Sam
fuente
1

Lanzar excepciones hace que la pila se desenrolle, lo que tiene algunos impactos en el rendimiento (admitido, los entornos administrados modernos han mejorado en eso). Sería una mala idea seguir lanzando y atrapando repetidamente excepciones en una situación anidada.

Probablemente más importante que eso, las excepciones son para condiciones excepcionales. No deben usarse para el flujo de control ordinario, ya que esto dañará la legibilidad de su código.

Arno
fuente
1

Tengo tres tipos de condiciones que atrapo.

  1. La entrada incorrecta o faltante no debería ser una excepción. Utilice tanto el js del lado del cliente como la expresión regular del lado del servidor para detectar, establecer atributos y reenviar a la misma página con mensajes.

  2. La AppException. Esta suele ser una excepción que detecta y agrega en su código. En otras palabras, estos son los que espera (el archivo no existe). Regístrese, configure el mensaje y reenvíe a la página de error general. Esta página generalmente tiene un poco de información sobre lo que sucedió.

  3. La excepción inesperada. Estos son los que no conoces. Regístrese con detalles y reenvíelos a una página de error general.

Espero que esto ayude

Miguel
fuente
1

La seguridad se combina con su ejemplo: no debe decirle a un atacante que existe un nombre de usuario, pero la contraseña es incorrecta. Esa es información adicional que no necesita compartir. Simplemente diga "el nombre de usuario o la contraseña son incorrectos".

luego
fuente
1

La respuesta simple es, siempre que una operación sea imposible (debido a cualquiera de las aplicaciones O porque violaría la lógica de negocios). Si se invoca un método y es imposible hacer para lo que fue escrito, lanzar una excepción. Un buen ejemplo es que los constructores siempre arrojan ArgumentExceptions si no se puede crear una instancia utilizando los parámetros proporcionados. Otro ejemplo es InvalidOperationException, que se produce cuando no se puede realizar una operación debido al estado de otro miembro o miembros de la clase.

En su caso, si se invoca un método como Iniciar sesión (nombre de usuario, contraseña), si el nombre de usuario no es válido, es correcto lanzar una UserNameNotValidException o PasswordNotCorrectException si la contraseña es incorrecta. El usuario no puede iniciar sesión con los parámetros proporcionados (es decir, es imposible porque violaría la autenticación), por lo que debe lanzar una excepción. Aunque podría tener sus dos Excepciones heredadas de ArgumentException.

Dicho esto, si NO desea lanzar una Excepción porque un error de inicio de sesión puede ser muy común, una estrategia es crear un método que devuelva tipos que representen errores diferentes. Aquí hay un ejemplo:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

A la mayoría de los desarrolladores se les enseña a evitar Excepciones debido a la sobrecarga causada por lanzarlos. Es genial ser consciente de los recursos, pero generalmente no a expensas del diseño de su aplicación. Esa es probablemente la razón por la que le dijeron que no lanzara sus dos Excepciones. Si usar Excepciones o no, generalmente se reduce a la frecuencia con que ocurrirá la Excepción. Si es un resultado bastante común o bastante esperado, es cuando la mayoría de los desarrolladores evitarán las Excepciones y, en su lugar, crearán otro método para indicar la falla, debido al supuesto consumo de recursos.

Aquí hay un ejemplo de cómo evitar usar Excepciones en un escenario como el que acabamos de describir, usando el patrón Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}
Chris
fuente
1

En mi opinión, la pregunta fundamental debería ser si uno esperaría que la persona que llama deseara continuar con el flujo normal del programa si ocurre una condición. Si no lo sabe, tenga métodos separados doSomething y trySomething, donde el primero devuelve un error y el segundo no, o tenga una rutina que acepte un parámetro para indicar si se debe lanzar una excepción si falla). Considere una clase para enviar comandos a un sistema remoto e informar respuestas. Ciertos comandos (por ejemplo, reiniciar) harán que el sistema remoto envíe una respuesta pero luego no responda durante un cierto período de tiempo. Por lo tanto, es útil poder enviar un comando "ping" y averiguar si el sistema remoto responde en un período de tiempo razonable sin tener que lanzar una excepción si no lo hace. t (la persona que llama probablemente esperaría que los primeros intentos de "ping" fallaran, pero uno eventualmente funcionaría). Por otro lado, si uno tiene una secuencia de comandos como:

  exchange_command ("abrir archivo temporal");
  exchange_command ("escribir datos del archivo temporal {lo que sea}");
  exchange_command ("escribir datos del archivo temporal {lo que sea}");
  exchange_command ("escribir datos del archivo temporal {lo que sea}");
  exchange_command ("escribir datos del archivo temporal {lo que sea}");
  exchange_command ("cerrar archivo temporal");
  exchange_command ("copiar archivo temporal a archivo real");

uno desearía que el fracaso de cualquier operación anule la secuencia completa. Si bien se puede verificar cada operación para asegurarse de que tuvo éxito, es más útil que la rutina exchange_command () arroje una excepción si falla un comando.

En realidad, en el escenario anterior puede ser útil tener un parámetro para seleccionar varios modos de manejo de fallas: nunca arroje excepciones, arroje excepciones solo por errores de comunicación o arroje excepciones en los casos en que un comando no devuelva un "éxito" " indicación.

Super gato
fuente
1

"PasswordNotCorrectException" no es un buen ejemplo para usar excepciones. Es de esperar que los usuarios tengan sus contraseñas incorrectas, por lo que no es una excepción en mi humilde opinión. Probablemente incluso se recupere de él, mostrando un buen mensaje de error, por lo que es solo una verificación de validez.

Las excepciones no controladas detendrán la ejecución eventualmente, lo cual es bueno. Si devuelve códigos falsos, nulos o de error, tendrá que lidiar con el estado del programa usted mismo. Si olvida verificar las condiciones en algún lugar, su programa puede seguir ejecutándose con datos incorrectos, y puede tener dificultades para averiguar qué sucedió y dónde .

Por supuesto, podría causar el mismo problema con las declaraciones catch vacías, pero al menos detectarlas es más fácil y no requiere que comprenda la lógica.

Entonces, como regla general:

Úselos donde no quiera o simplemente no pueda recuperarse de un error.

DanMan
fuente
0

Puede usar algunas excepciones genéricas para esas condiciones. Por ejemplo, ArgumentException está destinado a usarse cuando algo sale mal con los parámetros de un método (con la excepción de ArgumentNullException). En general, no necesitará excepciones como LessThanZeroException, NotPrimeNumberException, etc. Piense en el usuario de su método. El número de condiciones que ella querrá manejar específicamente es igual al número del tipo de excepciones que su método necesita lanzar. De esta manera, puede determinar qué tan detalladas serán las excepciones.

Por cierto, siempre trate de proporcionar algunas formas para que los usuarios de sus bibliotecas eviten excepciones. TryParse es un buen ejemplo, existe para que no tenga que usar int.Parse y detectar una excepción. En su caso, es posible que desee proporcionar algunos métodos para verificar si el nombre de usuario es válido o la contraseña es correcta para que sus usuarios (o usted) no tengan que manejar muchas excepciones. Con suerte, esto dará como resultado un código más legible y un mejor rendimiento.

Serhat Ozgel
fuente
0

En última instancia, la decisión se reduce a si es más útil lidiar con errores de nivel de aplicación como este mediante el manejo de excepciones, o mediante su propio mecanismo de inicio, como devolver códigos de estado. No creo que haya una regla estricta sobre cuál es mejor, pero consideraría:

  • ¿Quién llama tu código? ¿Es esta una API pública de algún tipo o una biblioteca interna?
  • Qué idioma estás usando? Si se trata de Java, por ejemplo, lanzar una excepción (marcada) pone una carga explícita en su interlocutor para manejar esta condición de error de alguna manera, en lugar de un estado de retorno que podría ignorarse. Eso podría ser bueno o malo.
  • ¿Cómo se manejan otras condiciones de error en la misma aplicación? Las personas que llaman no querrán lidiar con un módulo que maneje errores de manera idiosincrásica a diferencia de cualquier otra cosa en el sistema.
  • ¿Cuántas cosas pueden salir mal con la rutina en cuestión y cómo se manejarían de manera diferente? Considere la diferencia entre una serie de bloques catch que manejan diferentes errores y un cambio en un código de error.
  • ¿Tiene información estructurada sobre el error que necesita devolver? Lanzar una excepción le brinda un mejor lugar para poner esta información que simplemente devolver un estado.
eli
fuente
0

Hay dos clases principales de excepción:

1) Excepción del sistema (por ejemplo, conexión de base de datos perdida) o 2) Excepción del usuario. (por ejemplo, validación de entrada del usuario, 'la contraseña es incorrecta')

Me pareció útil crear mi propia clase de excepción de usuario y cuando quiero arrojar un error de usuario quiero que me manejen de manera diferente (es decir, se muestre un error de recursos al usuario), entonces todo lo que necesito hacer en mi controlador de errores principal es verificar el tipo de objeto :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If
Crujiente
fuente
0

Algunas cosas útiles para pensar al decidir si una excepción es apropiada:

  1. qué nivel de código desea que se ejecute después de que se produzca el candidato de excepción, es decir, cuántas capas de la pila de llamadas deberían desenrollarse. En general, desea manejar una excepción lo más cerca posible de donde ocurre. Para la validación de nombre de usuario / contraseña, normalmente manejaría las fallas en el mismo bloque de código, en lugar de dejar que aparezca una excepción. Entonces, una excepción probablemente no sea apropiada. (OTOH, después de tres intentos fallidos de inicio de sesión, el flujo de control puede cambiar a otra parte, y una excepción puede ser apropiada aquí).

  2. ¿Es este evento algo que le gustaría ver en un registro de errores? No todas las excepciones se escriben en un registro de errores, pero es útil preguntar si esta entrada en un registro de errores sería útil, es decir, intentaría hacer algo al respecto o sería la basura que ignora.

Mike Kantor
fuente