2008-09-14 8 views
33

Acabo de comenzar a rozar 'Debugging MS .Net 2.0 Applications' por John Robbins, y me he confundido por su evangelismo para Debug.Assert (...).Debug.Assert frente a excepciones arrojadas específicas

Señala que bien implementado Afirma almacenar el estado, en cierta medida, de una condición de error, por ejemplo:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter"); 

Ahora, personalmente, me parece una locura para mí, que él quiere reafirmar su prueba sin una comentario real de 'lógica de negocios' razonable, tal vez "i < = 3 nunca debe suceder debido al proceso de modificación de flobittyjam".

Entonces, creo que aparece Asserts como una clase de bajo nivel de "Vamos a proteger mis suposiciones" ... suponiendo que uno siente que esta es una prueba que solo se necesita hacer en la depuración, es decir, protegiéndose contra colegas y futuros programadores, y esperando que realmente prueben cosas.

Pero lo que no entiendo es que luego agrega que debe usar aserciones además del manejo normal de errores; ahora lo que preveo es algo como esto:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status"); 
if (i <= 3) 
{ 
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString()); 
} 

¿Qué he ganado por la repetición de la prueba Debug.Assert condición de error? Creo que lo conseguiría si estábamos hablando de depuración de sólo doble control de un cálculo muy importante ...

double interestAmount = loan.GetInterest(); 
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc"); 

... pero yo no lo entiendo para las pruebas de parámetros que son sin duda vale la pena comprobación (tanto en DEPURACIÓN como en compilaciones de versión) ... o no. ¿Qué me estoy perdiendo?

Respuesta

48

Las aserciones no son para la verificación de parámetros. La verificación de parámetros siempre debe hacerse (y de acuerdo con las condiciones previas especificadas en su documentación y/o especificación), y el ArgumentOutOfRangeException arrojado según sea necesario.

Las aserciones son para probar situaciones "imposibles", es decir, cosas que usted (en su lógica de programa) suponen que son verdaderas. Las afirmaciones están ahí para decirle si estas suposiciones se rompen por algún motivo.

Espero que esto ayude!

+10

Las afirmaciones pueden ser utilizados para la comprobación de parámetros de * interno * llamadas de método (llamado por código que pertenece a la misma componente) métodos, en oposición a las llamadas a métodos externos (llamados por otro componente) . Por ejemplo, podría afirmar que un parámetro de método privado de tipo Double no es un NaN. – RoadWarrior

+0

@Chris: Usted declara que las Afirmaciones no deben usarse para la verificación de parámetros. ¿Hay alguna razón para esto? Tiendo a lanzar excepciones para verificaciones de parámetros especialmente en constructores cuando inyecto objetos dependientes. Sin embargo, me están diciendo que use Afirmaciones. No tengo una explicación lógica para usar excepciones en lugar de aserciones. ¿Eres capaz de aclarar? Cheers –

+7

No se preocupe, descubrí por qué. De acuerdo con Jon Skeet, http://stackoverflow.com/questions/1276308/exception-vs-assertion: "Use las aserciones para las verificaciones lógicas internas dentro de su código, y las excepciones normales para las condiciones de error fuera del control de su código inmediato". –

2

IMO es solo una pérdida de tiempo de desarrollo. La excepción implementada correctamente le da una idea clara de lo que sucedió. Vi demasiadas aplicaciones que muestran oscuros errores "Error de aserción: i < 10". Veo la afirmación como una solución temporal. En mi opinión, ninguna afirmación debe estar en una versión final de un programa. En mi práctica usé aserciones para verificaciones rápidas y sucias. La versión final del código debe tener en cuenta la situación errónea y comportarse en consecuencia. Si sucede algo malo, tienes 2 opciones: manejarlo o dejarlo. La función debe arrojar una excepción con una descripción significativa si se pasan los parámetros incorrectos. No veo puntos en duplicación de la lógica de validación.

4

que utilizar cheques explícitas que generen excepciones en públicas y protegidas métodos y afirmaciones sobre los métodos privados.

Normalmente, las comprobaciones explícitas evitan que los métodos privados vean valores incorrectos de todos modos. Entonces, realmente, el afirmado está buscando una condición que debería ser imposible. Si una afirmación dispara, me dice que hay un defecto en la lógica de validación contenida dentro de una de las rutinas públicas en la clase.

17

Hay un aspecto de comunicación para afirmar frente a lanzamiento de excepción.

Digamos que tenemos una clase de usuario con una propiedad Name y un método ToString.

Si ToString se implementa como esto:

public string ToString() 
{ 
    Debug.Assert(Name != null); 
    return Name; 
} 

Se dice que nunca deberían Nombre nula y hay un error en la clase del usuario si lo es.

Si ToString es poner en práctica la siguiente manera:

public string ToString() 
{ 
    if (Name == null) 
    { 
      throw new InvalidOperationException("Name is null"); 
    } 

    return Name; 
} 

Se dice que la persona que llama está utilizando ToString incorrectamente si nombre es nulo y debe comprobar que antes de llamar.

La aplicación tanto con

public string ToString() 
{ 
    Debug.Assert(Name != null); 
    if (Name == null) 
    { 
      throw new InvalidOperationException("Name is null"); 
    } 

    return Name; 
} 

dice que si el nombre es nulo existe error en la clase de usuario, pero queremos manejar todos modos. (El usuario no necesita verificar el nombre antes de llamar). Creo que este es el tipo de seguridad que Robbins recomendaba.

3

Se puede atrapar y evitar una excepción, haciendo que el error sea invisible para la prueba. Eso no puede suceder con Debug.Assert.

Nadie debería tener un controlador catch que capte todas las excepciones, pero la gente lo hace de todos modos, y algunas veces es inevitable. Si se invoca su código desde COM, la capa de interoperabilidad captura todas las excepciones y las convierte en códigos de error COM, lo que significa que no verá las excepciones no controladas. Las afirmaciones no sufren de esto.

Además, cuando no se maneja la excepción, una práctica aún mejor es tomar un mini-volcado. Un área donde VB es más poderosa que C# es que puede usar un filtro de excepción para hacer un mini-volcado cuando la excepción está en vuelo, y dejar sin cambios el resto del manejo de excepciones. Gregg Miskelly's blog post on exception filter inject proporciona una forma útil de hacerlo desde C#.

Otra nota sobre los activos ... no interactúan mal con la Unidad probando las condiciones de error en su código. Vale la pena tener un contenedor para desactivar la afirmación de las pruebas unitarias.

0

Aquí está por 2 centavos.

Creo que la mejor manera es usar tanto aserciones como excepciones. Las principales diferencias entre los dos métodos, si las declaraciones de Assert se pueden eliminar fácilmente del texto de la aplicación (define, atributos condicionales ...), mientras que la Excepción lanzada depende (típicamente) de un código condicional que es más difícil de eliminar (sección múltiple con condicionales de preprocesador).

Cada excepción de aplicación debe manejarse correctamente, mientras que las afirmaciones se deben cumplir solo durante el desarrollo y prueba del algoritmo.

Si pasa una referencia de objeto nulo como parámetro de rutina y usa este valor, obtendrá una excepción de puntero nulo. De hecho: ¿por qué deberías escribir una afirmación? Es una pérdida de tiempo en este caso. Pero, ¿qué pasa con los miembros privados de la clase que se usan en las rutinas de la clase? Cuando estos valores se establecen en algún lugar, es mejor verificar con una aserción si se establece un valor nulo. Eso es solo porque cuando usa el miembro, obtiene una excepción de puntero nulo pero no sabe cómo se estableció el valor. Esto causa un reinicio del programa rompiendo todo el uso del punto de entrada para establecer el miembro privado.

Las excepciones son más útiles, pero pueden ser (muy) muy difíciles de gestionar y existe la posibilidad de utilizar demasiadas excepciones. Y requieren una verificación adicional, tal vez no deseada para optimizar el código. Personalmente utilizo excepciones solo cuando el código requiere un control de captura profundo (las declaraciones de captura son muy bajas en la pila de llamadas) o cuando los parámetros de la función no están codificados en el código.

4

He pensado mucho en esto cuando se trata de proporcionar orientación sobre depuración frente a afirmar con respecto a las preocupaciones de prueba.

Debería poder probar su clase con entradas erróneas, mal estado, orden de operaciones no válido y cualquier otra condición de error concebible y una afirmación debería nunca viaje. Cada afirmación está comprobando que algo debe siempre ser verdadero independientemente de las entradas o cálculos realizados.

buenas reglas generales que han llegado en:

  1. Afirma no son un reemplazo de código robusto que funciona correctamente independiente de la configuración. Ellos son complementarios.

  2. Las afirmaciones nunca se deben disparar durante una prueba de funcionamiento unitario, incluso cuando se introducen valores no válidos o condiciones de error de prueba. El código debe manejar estas condiciones sin que ocurra una afirmación.

  3. Si una afirmación se dispara (ya sea en una prueba unitaria o durante la prueba), la clase tiene errores.

Para todos los demás errores - por lo general hacia abajo para el medio ambiente (conexión de red perdida) o mal uso (el que llama pasa un valor nulo) - Es mucho más agradable y más comprensible para utilizar cheques duros & excepciones. Si ocurre una excepción, la persona que llama sabe que es probable que sea su culpa. Si se produce una afirmación, la persona que llama sabe que es probable que haya un error en el código donde se encuentra la declaración.

En cuanto a la duplicación: estoy de acuerdo. No veo por qué debería replicar la validación con un Debug.Assert Y una verificación de excepción. No solo agrega algo de ruido al código y enturbia las aguas con respecto a quién tiene la culpa, sino que es una forma de repetición.

1

Ejemplo de un buen uso de Assert:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry 
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning 

yo personalmente creo que afirman debe única ser utilizada cuando se sabe que algo está fuera deseables límites, pero puede estar seguro de que es razonablemente seguro continuar. En todas las demás circunstancias (no dude en señalar circunstancias en las que no he pensado) use las excepciones para fallar de manera rápida y difícil.

El principal inconveniente para mí es si desea derribar un sistema en vivo/producción con una excepción para evitar corrupción y facilitar la resolución de problemas, o si ha encontrado una situación que nunca debe continuar desapercibida en prueba/depurar versiones, pero se podría permitir que continúen en producción (registrando una advertencia, por supuesto).

cf.http://c2.com/cgi/wiki?FailFast copiado y modificado de la pregunta java: Exception Vs Assertion

Cuestiones relacionadas