personalizadas net handling exceptions example custom .net exception

.net - net - Siempre revisa los parámetros y lanza excepciones



exception personalizadas c# (8)

Bueno, eso depende. Si su código va a recoger el nulo de todos modos, y lanza una excepción, probablemente tenga más sentido asegurarse de tener un código de limpieza sensible. Si no se detecta lo contrario, o la limpieza puede tardar mucho tiempo, o puede haber una llamada fuera de proceso (por ejemplo, una base de datos), es mejor que no intentes cambiar el mundo de forma incorrecta y luego lo cambies.

¿Debería siempre verificar los parámetros y lanzar excepciones en .NET cuando los parámetros no son los que esperaba? Por ejemplo, objetos nulos o cadenas vacías?

Empecé a hacer esto, pero luego pensé que esto inflaría mucho mi código si se hace en todos los métodos. ¿Debo verificar los parámetros para los métodos privados y públicos?

Termino arrojando muchas excepciones de ArgumentNullException ("nombre") aunque el código que maneja la excepción no puede hacer nada diferente programáticamente ya que no hay garantía de que "nombre" no cambie en el futuro.

¿Asumo que esta información es útil cuando veo un registro lleno de información de excepción?

¿Es una buena práctica siempre "claro para lo peor"?


Depende del consumidor de su clase / método. Si todo es interno, diría que no es tan importante. Si tiene consumidores desconocidos / de terceros, entonces sí, es posible que desee una verificación exhaustiva.


El enfoque que tomo es verificar los argumentos y lanzar excepciones en los miembros públicamente visibles, cualquiera por visibles públicamente me refiero a los límites de ensamblaje externos (cualquier método public , protected o protected internal en una clase public . Esto se debe a que usted (típicamente) diseña un ensamblaje para funcionar como una unidad autónoma, de modo que cualquier cosa dentro de los límites de la asamblea debe seguir las reglas de cómo llamar a cualquier otra cosa allí.

Para cualquier miembro no públicamente visible (es decir, miembros o clases internal o private ) utilizo Debug.Assert para realizar la verificación en su lugar. De esta forma, si alguno de los llamantes dentro del ensamblado viola el contrato, lo averigua inmediatamente en el momento del desarrollo / prueba, pero no tiene una sobrecarga de rendimiento en la solución implementada final porque estas declaraciones se eliminan en las compilaciones RELEASE.


Gran parte de mi experiencia es con sistemas relativamente restringidos, donde el código de hinchazón de este tipo es indeseable. Por lo tanto, mi instinto es usar aserciones solo de depuración o dejarlo fuera por completo. Es de esperar que se produzcan posibles entradas no válidas durante las pruebas de la persona que llama que le proporcione los valores incorrectos, por lo que siempre que realice una prueba en modo de depuración y en el modo de lanzamiento, verá los diagnósticos. De lo contrario, depurará el bloqueo cuando eventualmente ocurra.

Si el tamaño del código y el rendimiento no importan (y en casi todos los códigos una simple comprobación de nulo o rango no afectará el rendimiento de todos modos), cuanto más afirme dejar en el código en modo de lanzamiento, más posibilidades tendrá de diagnosticar fallas sin necesidad de volver a crear el error en el modo de prueba. Esto puede ser un gran ahorro de tiempo. En particular, si su producto es una biblioteca, una proporción significativa de los informes de "fallas" se deben a errores de los clientes, por lo que ninguna cantidad de pruebas preliminares puede evitar que ocurran en la naturaleza. Cuanto antes pueda demostrarle al cliente que su código es incorrecto, antes podrá solucionarlo y podrá volver a encontrar sus propios errores.

Aún así, en C / C ++ encuentro que el caso específico de buscar punteros nulos es solo una ayuda menor. Si alguien le pasa un puntero, entonces la condición de validez completa no es "no debe ser nula". Necesita apuntar a la memoria que es legible (quizás también escribible) por el proceso actual a un cierto tamaño, y contiene el tipo correcto de objeto, posiblemente en un cierto subconjunto de todos los estados posibles. No necesita haber sido liberado, no haber sido destruido por un buffer rebasado en otro lugar, probablemente no para ser modificado simultáneamente por otro hilo, etc. Usted no va a probar todo eso en la entrada del método, por lo que aún puede perder el inválido parámetros. Cualquier cosa que lo lleve a usted u otros programadores a pensar que "este puntero no es nulo, por lo que debe ser válido", porque ha probado solo una pequeña parte de la condición de validez, es engañoso.

Si está pasando por un puntero, entonces ya se encuentra en un territorio donde necesita confiar en que la persona que llama no le dé basura. Rechazar una instancia particular de basura todavía te deja confiando en que la persona que llama no te dé ninguna de la miríada de otras clases de basura que pueden evocar que son más difíciles de detectar. Si encuentra que los punteros nulos son un tipo común de basura de sus interlocutores en particular, entonces de todos modos, pruébelos, ya que ahorra tiempo diagnosticando errores en otras partes del sistema. Depende de una evaluación de si encontrar errores en el código de la persona que llama con el síntoma "me pasa un puntero nulo", vale la pena hinchar tu propio código (quizás en tamaño binario, y ciertamente en el código fuente): si esos errores son raros, entonces tú " Probablemente esté perdiendo el tiempo y controle los bienes raíces para buscarlos.

Por supuesto, en algunos idiomas no se pasa de puntero y la persona que llama tiene solo oportunidades limitadas de dañar la memoria, por lo que hay menos margen para la basura. Pero en Java, por ejemplo, pasar el objeto incorrecto sigue siendo un error de programación más común que pasar un nulo erróneo. En cualquier caso, los nulos suelen ser bastante fáciles de diagnosticar si los dejas en el tiempo de ejecución para detectarlos, y observas la pila. Entonces, el valor de la verificación nula es bastante limitado incluso allí. En C ++ y C # puede usar pass-by-reference donde los nulos estarían prohibidos.

Lo mismo ocurre con cualquier otra entrada inválida específica que pueda probar, y cualquier idioma. Las pruebas completas previas y posteriores a la condición (si es posible) son, por supuesto, otra cuestión, ya que si puede probar todo el contrato de llamadas, entonces estará en un terreno mucho más firme. Y si puede usar el tejido o lo que sea para afirmar contratos sin agregar al código fuente de la función en sí, eso es aún mejor.


Mi filosofía para esta situación es notificar y seguir adelante (cuando corresponda). El pseudo-código sería:

if value == not_valid then #if DEBUG log failure value = a_safe_default_value #elsif RELASE throw #endif end

De esta forma, puede iterar fácilmente durante el desarrollo y hacer que los usuarios prueben su aplicación sin que sea un acto de frustración.


Mis dos centavos: todos sus métodos públicos siempre deben verificar la validez de los parámetros que se envían. Esto se suele llamar "programación por contrato" y es una forma excelente de detectar errores rápidamente y evitar propagarlos a través de sus funciones privadas que muchos argumentarían (incluyéndome a mí) no debería ser probado en unidades directamente. En cuanto a lanzar excepciones, si su función o programa no puede corregir el error en sí, debe arrojar una excepción porque ha sido arrojado a un estado no válido.


No hay nada peor que buscar un mensaje de ''referencia de objeto no establecido en una instancia de un objeto''. Si su código es lo suficientemente complejo, es difícil saber qué está fallando, especialmente en los sistemas de producción y especialmente si se encuentra en una condición de frontera rara. Una excepción explícita recorre un largo camino en la resolución de problemas. Es un dolor, pero es una de esas cosas de las que no te arrepentirás si algo malo sucede.


Para los métodos públicos , entonces sí: definitivamente es necesario verificar tus argumentos; para llamadas internas / privadas , Eric Lippert podría categorizarlas como "absurdas" ( aquí ); su consejo es no atraparlos ... ¡arregla el código!

Para evitar la hinchazón del código, es posible que desee considerar AOP, por ejemplo, postsharp . Para ilustrarlo, Jon Skeet tiene un atributo postesfá que hace una comprobación de nulo-argumento, aquí . Luego (para citar su ejemplo), simplemente puede atribuir el método:

[NullArgumentAspect("text")] public static IEnumerable<char> GetAspectEnhancedEnumerable(string text) { /* text is automatically checked for null, and an ArgumentNullException thrown */ }

Otro truco útil aquí podría ser métodos de extensión; los métodos de extensión tienen la característica curiosa de que puede llamarlos en instancias nulas ... para que pueda hacer lo siguiente (donde el uso de genéricos en lugar de "objetos" es para no llamarlo accidentalmente al encapsular un tipo de valor) :

static void ThrowIfNull<T>(this T value, string name) where T : class { if (value == null) throw new ArgumentNullException(name); } // ... stream.ThrowIfNull("stream");

Y puede hacer cosas similares con fuera de rango, etc.