try tipos sintaxis que propiedades practicas para manejo las lanzar existen excepciones excepcion errores ejemplos como catch capturar buenas c# winforms exception-handling

c# - tipos - La mejor práctica para el manejo de excepciones en una aplicación de Windows Forms?



try catch c# (15)

Actualmente estoy en el proceso de escribir mi primera aplicación de Windows Forms. He leído algunos libros de C # ahora, así que tengo una comprensión relativamente buena de las características del lenguaje C # para lidiar con las excepciones. Sin embargo, todos son bastante teóricos, por lo que lo que aún no tengo es una idea de cómo traducir los conceptos básicos en un buen modelo de manejo de excepciones en mi aplicación.

¿Alguien le gustaría compartir alguna perla de sabiduría sobre el tema? Publique cualquier error común que haya visto que los novatos como yo hagamos, y cualquier consejo general sobre el manejo de excepciones de forma que mi aplicación sea más estable y robusta.

Las principales cosas que estoy tratando de resolver son:

  • ¿Cuándo debería volver a lanzar una excepción?
  • ¿Debo tratar de tener un mecanismo central de manejo de errores de algún tipo?
  • ¿El manejo de las excepciones que se pueden lanzar tiene un impacto en el rendimiento en comparación con las pruebas preventivas, como si existe un archivo en el disco?
  • ¿Debería incluirse todo el código ejecutable en los bloques try-catch-finally?
  • ¿Hay algún momento en que un bloque de captura vacío podría ser aceptable?

Todos los consejos recibidos con gratitud


¿Cuándo debería volver a lanzar una excepción?

En todas partes, pero los métodos del usuario final ... como el botón haga clic en controladores

¿Debo tratar de tener un mecanismo central de manejo de errores de algún tipo?

Escribo un archivo de registro ... bastante fácil para una aplicación WinForm

¿El manejo de las excepciones que se pueden lanzar tiene un impacto en el rendimiento en comparación con las pruebas preventivas, como si existe un archivo en el disco?

No estoy seguro de esto, pero creo que es una buena práctica echarle un vistazo a las excepciones ... quiero decir que puedes preguntar si existe un archivo y si no arroja una excepción FileNotFoundException

¿Debería incluirse todo el código ejecutable en los bloques try-catch-finally?

yeap

¿Hay algún momento en que un bloque de captura vacío podría ser aceptable?

Sí, digamos que quieres mostrar una fecha, pero no tienes idea de cómo esa fecha era almacenada (dd / mm / aaaa, mm / dd / aaaa, etc.) intentas analizarla, pero si falla, continúa. .si es irrelevante para ti ... diría que sí, que hay


Al volver a lanzar una excepción, la palabra clave se tira sola. Esto lanzará la excepción atrapada y aún podrá usar el seguimiento de pila para ver de dónde vino.

Try { int a = 10 / 0; } catch(exception e){ //error logging throw; }

Hacer esto hará que el seguimiento de la pila termine en la declaración catch. (evitar esto)

catch(Exception e) // logging throw e; }


Aquí hay algunas pautas que sigo

  1. Fail-Fast: esta es más una excepción que genera una guía, por cada suposición que haga y cada parámetro que esté ingresando en una función, haga una verificación para asegurarse de que está comenzando con los datos correctos y que las suposiciones están haciendo son correctos. Las verificaciones típicas incluyen, argumento no nulo, argumento en rango esperado, etc.

  2. Al reiniciar, preservar el seguimiento de la pila: esto simplemente se traduce en utilizar throw cuando se reinicia en lugar de arrojar una nueva excepción (). Alternativamente, si cree que puede agregar más información, ajuste la excepción original como una excepción interna. Pero si está capturando una excepción solo para iniciar sesión, definitivamente use throw;

  3. No capte las excepciones que no puede manejar, así que no se preocupe por cosas como OutOfMemoryException, ya que de lo contrario no podrá hacer mucho.

  4. Enganche a los manejadores de excepciones globales y asegúrese de registrar tanta información como sea posible. Para winforms, enganche tanto el dominio de aplicación como el hilo de eventos de excepción no controlados.

  5. El rendimiento solo se debe tener en cuenta cuando se analiza el código y se ve que está causando un cuello de botella en el rendimiento, optimizando de forma predeterminada la legibilidad y el diseño. Entonces, acerca de su pregunta original en el archivo de verificación de existencia, yo diría que depende, si puede hacer algo para que el archivo no esté allí, entonces sí, compruebe lo contrario, si todo lo que va a hacer es lanzar una excepción si el archivo está no allí, entonces no veo el punto.

  6. Definitivamente hay momentos en que se requieren bloques de captura vacíos, creo que las personas que dicen lo contrario no han trabajado en las bases de código que han evolucionado en varios lanzamientos. Pero deben ser comentados y revisados ​​para asegurarse de que sean realmente necesarios. El ejemplo más típico es el de los desarrolladores que usan try / catch para convertir cadenas en números enteros en lugar de usar ParseInt ().

  7. Si espera que la persona que llama de su código pueda manejar las condiciones de error, cree excepciones personalizadas que detallen la situación no superada y proporcione información relevante. De lo contrario, solo adhiérase a los tipos de excepciones incorporadas tanto como sea posible.


Aquí hay un excelente código de artículo de CodeProject . Aquí hay un par de aspectos destacados:

  • Planifique lo peor *
  • Compruébalo temprano
  • No confíes en los datos externos
  • Los únicos dispositivos confiables son: el video, el mouse y el teclado.
  • Las escrituras también pueden fallar
  • Código de forma segura
  • No arroje nueva Excepción ()
  • No coloque información de excepción importante en el campo Mensaje
  • Ponga una sola captura (Excepción ex) por hilo
  • Las excepciones genéricas capturadas deben publicarse
  • Log Exception.ToString (); nunca registre Exception.Message!
  • No atrape (Excepción) más de una vez por hilo
  • No tragues nunca excepciones
  • El código de limpieza debería ponerse finalmente en bloques
  • Use "usar" en todas partes
  • No devuelva valores especiales en condiciones de error
  • No use excepciones para indicar la ausencia de un recurso
  • No use el manejo de excepciones como medio para devolver información de un método
  • Use excepciones para errores que no deben ser ignorados
  • No borre la traza de pila al volver a lanzar una excepción
  • Evite cambiar excepciones sin agregar valor semántico
  • Las excepciones deben marcarse [Serializable]
  • En caso de duda, no declare, envíe una excepción
  • Cada clase de excepción debe tener al menos los tres constructores originales
  • Tenga cuidado al usar el evento AppDomain.UnhandledException
  • No reinventar la rueda
  • No use manejo de errores no estructurados (VB.Net)

En mi experiencia, he visto oportuno detectar excepciones cuando sé que las voy a crear. Por ejemplo, cuando estoy en una aplicación web y realizo un Response.Redirect, sé que obtendré System.ThreadAbortException. Como es intencional, simplemente tengo un truco para el tipo específico y simplemente me lo trago.

try { /*Doing stuff that may cause an exception*/ Response.Redirect("http://www.somewhereelse.com"); } catch (ThreadAbortException tex){/*Ignore*/} catch (Exception ex){/*HandleException*/}


Estoy a punto de salir, pero le daré un breve resumen sobre dónde usar el manejo de excepciones. Intentaré abordar sus otros puntos cuando regrese :)

  1. Verificar explícitamente todas las condiciones de error conocidas *
  2. Agregue un try / catch alrededor del código si no está seguro si pudo manejar todos los casos
  3. Agregue un try / catch alrededor del código si la interfaz .NET a la que llama arroja una excepción
  4. Agregue un try / catch alrededor del código si cruza un umbral de complejidad para usted
  5. Agregue un try / catch alrededor del código para una verificación de cordura: Usted afirma que ESTO NO DEBE SUCEDER
  6. Como regla general, no utilizo las excepciones como reemplazo de los códigos de retorno. Esto está bien para .NET, pero yo no. Sin embargo, tengo excepciones (jeje) a esta regla, depende de la arquitectura de la aplicación en la que esté trabajando también.

*Dentro de lo razonable. No es necesario verificar si, por ejemplo, un rayo cósmico golpea sus datos, lo que provoca que se vuelquen un par de bits. Comprender qué es "razonable" es una habilidad adquirida para un ingeniero. Es difícil de cuantificar, pero fácil de intuir. Es decir, puedo explicar fácilmente por qué utilizo un try / catch en cualquier instancia en particular, sin embargo, estoy en la imposibilidad de imbuir a otro con este mismo conocimiento.

Por mi parte, tienden a alejarse de arquitecturas fuertemente basadas en excepciones. try / catch no tiene un hit de rendimiento como tal, el hit aparece cuando se lanza la excepción y el código puede tener que subir varios niveles de la pila de llamadas antes de que algo lo maneje.


Estoy profundamente de acuerdo con la regla de:

  • Nunca permita que los errores pasen desapercibidos.

La razón es que:

  • Cuando escribe el código por primera vez, lo más probable es que no tenga el conocimiento completo del código tripartito, .NET FCL lirary o las últimas contribuciones de sus compañeros de trabajo. En realidad, no puedes negarte a escribir código hasta que conozcas bien todas las posibilidades de excepción. Asi que
  • Constantemente encuentro que uso try / catch (Exception ex) solo porque quiero protegerme de cosas desconocidas y, como habrás notado, capturo Exception, no la más específica, como OutOfMemoryException, etc. Y siempre hago la excepción me apareció (o el control de calidad) por ForceAssert.AlwaysAssert (false, ex.ToString ());

ForceAssert.AlwaysAssert es mi forma personal de Trace.Assert solo independientemente de si se define la macro DEBUG / TRACE.

El ciclo de desarrollo puede ser: noté el desagradable diálogo de Assert u otra persona se quejó de ello, luego volví al código y descubrí el motivo para plantear la excepción y decidir cómo procesarla.

De esta forma puedo escribir mi código en poco tiempo y protegerme de un dominio desconocido, pero siempre notado si sucedieron cosas anormales, de esta manera el sistema se volvió seguro y más seguro.

Sé que muchos de ustedes no estarán de acuerdo conmigo porque un desarrollador debe conocer cada detalle de su código, francamente, también soy un purista en los viejos tiempos. Pero hoy aprendí que la política anterior es más pragmática.

Para el código de WinForms, una regla de oro que siempre obedezco es:

  • Siempre intente / capture (Excepción) su código de controlador de eventos

esto protegerá su UI siendo siempre utilizable.

Para el golpe de rendimiento, la penalización de rendimiento solo ocurre cuando el código alcanza la captura, la ejecución del código de prueba sin la excepción real planteada no tiene un efecto significativo.

La excepción debe ocurrir con pocas posibilidades, de lo contrario no son excepciones.


La regla de oro que se ha intentado mantener es manejar la excepción lo más cerca posible de la fuente.

Si debe volver a lanzar una excepción, intente agregarla, volver a lanzar una excepción FileNotFoundException no ayuda mucho, pero lanzar una ConfigurationFileNotFoundException le permitirá capturar y actuar en algún lugar de la cadena.

Otra regla que trato de seguir es no usar try / catch como una forma de flujo de programa, así que verifico los archivos / conexiones, aseguro que los objetos se hayan iniciado, etc. antes de usarlos. Try / catch debería ser para Exceptions, cosas que no puedes controlar.

En cuanto a un bloque de catch vacío, si está haciendo algo de importancia en el código que generó la excepción, debe volver a lanzar la excepción como mínimo. Si no hay consecuencias del código que arrojó la excepción que no se ejecuta, ¿por qué lo escribió en primer lugar?


Las excepciones son costosas pero necesarias. No necesita envolver todo en una captura de prueba, pero sí necesita asegurarse de que las excepciones siempre se capturen. Gran parte de esto dependerá de tu diseño.

No vuelva a lanzar si dejar que la excepción aumente también lo hará. Nunca permita que los errores pasen desapercibidos.

ejemplo:

void Main() { try { DoStuff(); } catch(Exception ex) { LogStuff(ex.ToString()); } void DoStuff() { ... Stuff ... }

Si DoStuff sale mal, querrá que fianza de todos modos. La excepción se lanzará a main y verá el tren de eventos en la pila de ex.


Lo único que aprendí muy rápido fue incluir absolutamente todo pedazo de código que interactúa con algo fuera del flujo de mi programa (es decir, sistema de archivos, llamadas a bases de datos, entrada de usuario) con bloques try-catch. Try-catch puede incurrir en un golpe de rendimiento, pero generalmente en estos lugares en su código no se notará y se pagará por sí mismo con seguridad.

He usado bloques de captura vacíos en lugares donde el usuario podría hacer algo que no es realmente "incorrecto", pero puede arrojar una excepción ... un ejemplo que viene a la mente es en un GridView si el usuario hace doble clic en el marcador de posición gris celda en la parte superior izquierda disparará el evento CellDoubleClick, pero la celda no pertenece a una fila. En ese caso, no es necesario que publique un mensaje, pero si no lo detecta arrojará un error de excepción no controlada al usuario.


Me gusta la filosofía de no detectar nada que no tenga la intención de manejar, cualquiera que sea el manejo en mi contexto particular.

Odio cuando veo código como:

try { // some stuff is done here } catch { }

He visto esto de vez en cuando y es bastante difícil encontrar problemas cuando alguien ''come'' las excepciones. Un compañero de trabajo que tuve hace esto y tiende a ser un contribuyente a un flujo constante de problemas.

Vuelvo a lanzar si hay algo que mi clase en particular tiene que hacer en respuesta a una excepción, pero el problema se debe eliminar, pero se llama el método donde ocurrió.

Creo que el código debe escribirse de manera proactiva y que las excepciones deben ser para situaciones excepcionales, no para evitar las pruebas de condiciones.


Tenga en cuenta que Windows Forms tiene su propio mecanismo de manejo de excepciones. Si se hace clic en un botón en forma y su manejador lanza una excepción que no está atrapada en el manejador, Windows Forms mostrará su propio Diálogo de excepción no controlada.

Para evitar que se muestre el Diálogo de excepciones no administradas y detectar tales excepciones para el registro y / o para proporcionar su propio cuadro de diálogo de error, puede adjuntarlo al evento Application.ThreadException antes de llamar a Application.Run () en su método Main ().


Tienes que pensar en el usuario. El bloqueo de la aplicación es lo último que quiere el usuario. Por lo tanto, cualquier operación que pueda fallar debe tener un bloque try catch en el nivel ui. No es necesario usar try catch en cada método, pero cada vez que el usuario hace algo debe poder manejar excepciones genéricas. Eso de ninguna manera lo libera de revisar todo para evitar excepciones en el primer caso, pero no hay una aplicación compleja sin errores y el sistema operativo puede agregar fácilmente problemas inesperados, por lo tanto, debe anticiparse a lo inesperado y asegurarse de que el usuario quiera usar uno operación no habrá pérdida de datos porque la aplicación falla. No hay necesidad de dejar que la aplicación se bloquee, si detecta excepciones, nunca estará en un estado indeterminado y el usuario SIEMPRE se verá incomodado por un bloqueo. Incluso si la excepción se encuentra en el nivel más alto, no fallar significa que el usuario puede reproducir rápidamente la excepción o, al menos, registrar el mensaje de error y, por lo tanto, puede ayudarlo a solucionar el problema. Ciertamente, mucho más que obtener un mensaje de error simple y luego ver solo el cuadro de diálogo de error de Windows o algo así.

Es por eso que NUNCA debes ser presumido y pensar que tu aplicación no tiene errores, eso no está garantizado. Y es un esfuerzo muy pequeño para ajustar algunos bloques de captura de prueba sobre el código apropiado y mostrar un mensaje de error / registrar el error.

Como usuario, ciertamente me cabreo seriamente cada vez que una aplicación de brows o office o lo que sea se cuelga. Si la excepción es tan alta que la aplicación no puede continuar, es mejor mostrar ese mensaje y decirle al usuario qué hacer (reiniciar, corregir algunas configuraciones de sistema operativo, informar el error, etc.) que simplemente bloquear y eso es todo.


Todos los consejos publicados aquí hasta ahora son buenos y vale la pena prestar atención.

Una cosa en la que me gustaría expandir es su pregunta: "¿El manejo de las excepciones que se pueden lanzar tiene un impacto en el rendimiento en comparación con las pruebas preventivas, como si existe un archivo en el disco?"

La regla empírica ingenua es "los bloques de prueba / captura son caros". Eso no es verdad. Probar no es costoso. Es la captura, donde el sistema tiene que crear un objeto Exception y cargarlo con el seguimiento de la pila, que es caro. Hay muchos casos en los que la excepción es, bueno, lo suficientemente excepcional como para ajustar el código en un bloque try / catch.

Por ejemplo, si está poblando un diccionario, esto:

try { dict.Add(key, value); } catch(KeyException) { }

a menudo es más rápido que hacer esto:

if (!dict.ContainsKey(key)) { dict.Add(key, value); }

por cada elemento que agregue, porque la excepción solo se lanza cuando agrega una clave duplicada. (Las consultas agregadas de LINQ hacen esto).

En el ejemplo que diste, usaría try / catch casi sin pensar. En primer lugar, solo porque el archivo exista cuando lo compruebe no significa que va a existir cuando lo abra, por lo que realmente debería manejar la excepción de todos modos.

Segundo, y creo que más importante, a menos que a) su proceso esté abriendo miles de archivos yb) las probabilidades de que un archivo intente abrir no existente no sea trivialmente bajo, el rendimiento de crear la excepción no es algo que usted '' re alguna vez va a notar. En términos generales, cuando su programa intenta abrir un archivo, solo intenta abrir un archivo. Ese es un caso en el que escribir un código más seguro es casi seguro que va a ser mejor que escribir el código más rápido posible.


Unos pocos bits más ...

Debería tener una política de gestión de excepciones centralizada en su lugar. Esto puede ser tan simple como envolver Main() en un try / catch, failing fast con un elegante mensaje de error para el usuario. Este es el controlador de excepción de "último recurso".

Las comprobaciones preventivas son siempre correctas si es posible, pero no siempre son perfectas. Por ejemplo, entre el código donde verifica la existencia de un archivo y la siguiente línea donde lo abre, el archivo podría haberse eliminado o algún otro problema puede impedir su acceso. Aún necesitas probar / atrapar / finalmente en ese mundo. Use tanto el control preventivo como el try / catch / finally según corresponda.

Nunca "trague" una excepción, excepto en los casos más bien documentados cuando esté absoluta y absolutamente seguro de que la excepción arrojada es vivible. Esto casi nunca será el caso. (Y si lo es, asegúrese de tragar solo la clase de excepción específica ; no trague nunca System.Exception ).

Al construir bibliotecas (usadas por su aplicación), no trague las excepciones, y no tema dejar que las excepciones salgan a relucir. No vuelva a tirar a menos que tenga algo útil que agregar. Nunca (en C #) haga esto:

throw ex;

Como borrará la pila de llamadas. Si debe volver a lanzar (lo que a veces es necesario, como cuando usa el Bloque de manejo de excepciones de Enterprise Library), use lo siguiente:

throw;

Al final del día, la gran mayoría de las excepciones lanzadas por una aplicación en ejecución deberían estar expuestas en alguna parte. No deben exponerse a los usuarios finales (ya que a menudo contienen datos de propiedad o de otro tipo valiosos), sino que generalmente se registran y se notifica a los administradores de la excepción. Al usuario se le puede presentar un cuadro de diálogo genérico, tal vez con un número de referencia, para mantener las cosas simples.

El manejo de excepciones en .NET es más arte que ciencia. Todos tendrán sus favoritos para compartir aquí. Estos son solo algunos de los consejos que he recogido usando .NET desde el día 1, técnicas que han salvado mi tocino en más de una ocasión. Su experiencia puede ser diferente.