2009-02-03 8 views
16

sorprendente que sólo fue capaz de encontrar uno en la pregunta anterior SO sobre este tema, y ​​yo sólo quisiera obtener el "voto de confianza" de la comunidad (o no!) En mi enfoque.Debug.Assert vs excepciones

La forma en que veo que es así:

  • uso Debug.Assert decir las cosas que se pueden esperar sería verdad. Esto se usaría cuando tengamos un control total sobre nuestro entorno, por ejemplo, en un método verifique algunas condiciones previas y posteriores.
  • use Excepciones cuando surgen circunstancias excepcionales. Tratar con recursos externos, es decir, archivos, bases de datos, redes, etc. es una obviedad. Pero ...

Se pone un poco turbio en el siguiente escenario. Tenga en cuenta que este es un EJEMPLO CONTRARIO solo para ilustración.

Digamos que tenemos class MiClase, que tiene un Mi modo la propiedad pública y un método GetSomeValueForCurrentMode(). Considere MyClass como uno destinado a ser enviado (lanzamiento incorporado) en una biblioteca para uso de otros desarrolladores.

Esperamos Mi modo de ser actualizado por los usuarios externos de esta clase. Ahora, GetSomeValueForCurrentMode() tiene la siguiente lógica:

switch(MyMode) 
{ 
case Mode.ModeA: 
return val1; 
case Mode.ModeB: 
return val2; 
default: 
//Uh-uh this should never happen 

} 

Lo que quiero decir aquí es que el usuario de MiClase ha dejado en un estado no válido. ¿Entonces, qué debemos hacer?

En el valor predeterminado, ¿deberíamos Debug.Assert o throw new InvalidOperationException (u otro)?

Hay un mantra que dice que no debemos confiar en los usuarios de nuestras clases. Si seleccionamos Debug.Assert y construimos MyClass como una versión de lanzamiento (eliminando así las Aserciones de depuración), el usuario de la clase no obtendría información útil de que la había dejado en un estado no válido. Pero es un poco contrario al otro mantra que dice que solo arroja excepciones cuando suceden cosas completamente fuera de tu control.

encuentro que dar vueltas con esto - uno de esos debates de programación que no parecen tener una respuesta definitiva 'correcta'. ¡Así que pongámoslo a votación!

Editar: me di cuenta de esta respuesta en una cuestión de forma relacionada (Design by contract using assertions or exceptions?):

La regla de oro es que usted debe usar aseveraciones cuando se está tratando de atrapar sus propios errores y excepciones cuando se trata de captar los errores de otras personas En otras palabras, debe usar excepciones para verificar las condiciones previas para las funciones API públicas, y cada vez que obtenga datos que sean externos a su sistema. Debe usar afirmaciones para las funciones o datos que son internos a su sistema.

Para mí, esto tiene sentido, y se puede combinar con la técnica 'Assert then throw' descrita a continuación.

Pensamientos ¡bienvenidos!

Respuesta

5

Estoy de acuerdo con la mayoría de la gente aquí y sigo Design-by-Contract. Debe intentar y diferenciar muy claramente entre los requisitos del código implementado (Contratos) y descifrar el estado esperado durante el diseño (Afirmaciones de depuración).

SIEMPRE debe arrojar aserciones de contrato como excepciones (ya que siempre deben ser excepcionales). Hay mecanismos integrados en la mayoría de los marcos para capturar aserciones de depuración. Pero en el tiempo de ejecución siempre deberías lanzar una excepción.

Utilizo una biblioteca personalizada para ayudar con esto (en C#/VB.NET). Recientemente lo presenté en Codeplex (http://www.contractdriven.com/) si le interesa cómo funciona esto en la práctica.

Una ventaja adicional de esto es que a medida que comienzas a utilizar DbC de forma más regular, casi nunca necesitas utilizar las aserciones de depuración ya que hay garantías explícitas escritas en tu código, por lo que es difícil entrar en un estado no válido.

Así que la pregunta en su publicación original ... "Lo que estoy obteniendo aquí es que el usuario de MyClass lo ha dejado en un estado no válido. Entonces, ¿qué deberíamos hacer?" ... nunca debería surgir.

¡Es posible que nunca tenga que volver a depurar nada! ;-)

+2

Chris, arrojar una excepción de "contrato roto" (que solo puede indicar un error) no es "fallido rápido", es "error medio rápido": primero tiene que suceder un montón de desenrollado de la pila. Abortar allí mismo cuando detecte el incumplimiento de contrato sería el verdadero "fracaso rápido". –

+1

Y. Lo siento todos. Quité la referencia a "Fail Fast". Si desea utilizar FailFast, puede usar Environment.FailFast() en C# en lugar de lanzar una excepción. Pero ten en cuenta que tus bloques finalmente no se ejecutarán. – ChrisV

+0

Considere el uso de [fluentassertions] (http://fluentassertions.com/). Ayuda a crear contratos legibles y al mismo tiempo arroja excepciones si se violan los contratos. –

0

Depende de la lengua, es afirmar si el azúcar sintaxis continuación, se debe utilizar. sin embargo, en Java afirma que debe activarse para que funcione, por lo que la excepción es mejor. Sin embargo, siempre es mejor tener una excepción específica, por lo que aquí debería ser IllegalStateException.

0

En .Net, los métodos de clase Debug no están incluidos en su programa cuando realiza una compilación de lanzamiento, por lo que tendrá que lanzar una excepción si este código llega a la producción.

2

Mi uso de afirmaciones sigue las ideas de design by contract. Básicamente, afirma que los parámetros entrantes, variables globales y otra información de estado son válidos a medida que ingresa su función, y afirma que los valores de retorno y el estado también son válidos a medida que se va. Si obtiene una afirmación fallida al inicio, es un error en el código de llamada, si la obtiene al final, el error está en este código. Estas son condiciones previas y condiciones posteriores.

Una afirmación en su declaración de cambio solo es realmente útil si ha verificado sus condiciones previas al ingresar. Si llega a un estado inaceptable en el medio de su función en este escenario, es un error en su función o función llamada. Personalmente, me quedaría con una afirmación aquí ya que no es una excepción. Como implica, las excepciones se relacionan con los recursos, no con los errores. Sin embargo, puede crear un controlador de aserciones personalizado que exista en la compilación de lanzamiento y lanzar una excepción en caso de error, para darle a su programa la oportunidad de volver a un estado estable.

+0

¿Se queda con Asserts incluso si MyClass se empaqueta y se publica como una biblioteca para otros desarrolladores? – Duncan

+0

Sí. Si MyClass está en un estado inesperado, se debe a un error interno o a un uso incorrecto (que no cumple con las condiciones previas). No lanzaría excepciones para ninguno de estos. Para el primero, devolvería un error de parámetro malo. Para este último, incluyo afirmaciones en un modo de diagnóstico de lanzamiento. –

5

En primer lugar, MyClass es válido, por supuesto, debe ser expresado por MyClass invariante.

En segundo lugar, usted dice "Esperamos Mi modo de ser actualizado por los usuarios externos de esta clase" -, por supuesto, el colocador de este modo debe tener la forma típica de diseño por contrato (como cualquier función pública):

void Setter(mode m) 
    { 
    // INVARIANT ASSERT (1) 
    // PRECONDITION ASSERTS (uses "m") (2) 

    // BODY (3) 

    // POSTCONDITION ASSERTS (if any) (4) 
    // INVARIANT ASSERT (5) 
    } 

En (5) fallaría con una violación de aserción gritando que el invariante no contiene. Pero en (2) fallarías antes, porque el modo m pasado no es válido. Esto enviaría un mensaje claro al usuario y, por lo tanto, resuelve su problema.

Y no me digas que el campo de modo es público y los usuarios lo cambian sin ningún tipo de control.

Editar: Sobre afirmaciones y modo de lanzamiento, ver también:

4

A menudo ambos: afirmar, luego tirar.

Lo afirma porque desea notificar a los desarrolladores una suposición errónea durante el desarrollo.

Lanzas porque si esto ocurre en una compilación de lanzamiento, debes asegurarte de que el sistema no continúe procesando mientras está en mal estado.

Las características de confiabilidad deseadas de su sistema pueden afectar su elección aquí, pero 'assert then throw' es a menudo una estrategia útil, creo.

+0

Gracias, eso tiene mucho sentido y es similar a mi forma de pensar. Dejaré en manos de la comunidad SO para decidir si están de acuerdo. – Duncan

+1

Brian, no estoy de acuerdo. La afirmación está ahí para ayudarte a probar tu código. Si se salta la afirmación en producción, significa que tiene una ruta de código no comprobada, y no hay garantía de que su manejo de excepciones más abajo en la pila también se pruebe. –

+1

Es cierto. Una vez más, las características de confiabilidad del sistema entran en juego. Si está trabajando en un servidor web, es posible que haya diseñado y revisado el manejo de excepciones del sistema hasta la muerte (para mantener el tiempo de actividad del servidor pero permita que las cosas salgan mal con un mensaje/sesión), en cuyo caso puede tener. .. – Brian

5

básicamente estoy de acuerdo con la conclusión de su propia pregunta: si el código Un de piojos detecta un error A piojos hacen, que es un caso de Un ssert (y afirmaciones se debe dejar encendido en el código de producción, a menos el rendimiento dicta lo contrario). Si el código de Alicia detecta un error en E ve código, es un caso para E xcepciones, suponiendo que Alice y Eve están en lados opuestos de su software de seguimiento de errores.

Ahora que es una regla general. Las afirmaciones, en una forma ligeramente modificada, también se pueden usar como "heads-up, desarrollador!" mecanismo (y luego no deberían llamarse "ASSERT" sino "HEADS_UP" o algo similar). ¿Qué sucede si su empresa desarrolla un producto cliente/servidor y el servidor envía datos no válidos al cliente? Si usted es un programador cliente, tiene ganas de tratarlo como datos externos (son los datos de Eve los que están kaputt) y desea lanzar una excepción. Pero una afirmación "más suave", que hace que el depurador de Visual Studio se detenga ahí mismo, puede ser una muy buena forma de detectar esos problemas muy temprano y reportarlo al equipo del servidor. En una instalación real, podría ser que Mallory atempere con los datos entre Eve y Alice, pero la mayoría de las veces es un error de uno de tus colegas, y quieres verlo cuando sucede, es por eso que los llamo Afirmaciones "heads-up": no reemplazan las excepciones, pero te dan una advertencia y una oportunidad de inspeccionar el problema.

1

Yo diría: use una excepción si el error está en el código de otra persona, o en el código en un subsistema diferente (ya sea que lo haya escrito o no). También use una excepción si hay un error en los datos que provienen de una fuente externa, como un archivo.

A veces, no sabe si los datos provienen de una fuente externa o no. Si la seguridad es importante y existe la posibilidad de que esté tratando con datos externos que no se han verificado como correctos, use una excepción. Si la seguridad no es importante, entonces usaría el rendimiento como un desempate: ¿se podría ejecutar este código en un ciclo cerrado? De ser así, considere usar una afirmación, ya que obtendrá un mejor rendimiento en una compilación de versión. De lo contrario, use una excepción.

Cuestiones relacionadas