2012-04-12 14 views
5

Recientemente tuve un error de codificación donde bajo ciertas condiciones una variable no se inicializaba y obtenía un NullReferenceException. Esto demoró un tiempo en depurarse ya que tuve que encontrar los bits de datos que generarían esto para recrear el error ya que la excepción no da el nombre de la variable.Una mejor manera de manejar NullReferenceExceptions en C#

Obviamente, pude verificar cada variable antes de su uso y arrojar una excepción informativa, pero ¿hay alguna forma mejor (leer menos codificación) de hacer esto? Otro pensamiento que tuve fue el envío con los archivos pdb para que la información del error contenga la línea de código que causó el error. ¿Cómo otras personas evitan/manejan este problema?

Gracias

+1

Always 'Assert' generosamente para valores nulos. – leppie

+0

@leppie - ¡eso es mucho afirmar! Sí, podría hacerlo, pero esperaba una solución más elegante para tener en cuenta situaciones como que me olvidé de hacer eso :) –

+1

Protip: mantén tus métodos pequeños (digamos un máximo de 10-20 líneas), entonces realmente no necesitas números de línea . – leppie

Respuesta

11

En primer lugar: no hacer demasiado en un solo estado. Si tiene un gran número de operaciones de desreferenciación en una línea, va a ser mucho más difícil de encontrar al culpable. El Law of Demeter ayuda con esto también - si tiene algo como order.SalesClerk.Manager.Address.Street.Length, entonces tiene muchas opciones que recorrer cuando obtiene una excepción. (Estoy no dogmática sobre la Ley de Demeter, pero todo con moderación ...)

En segundo lugar: Prefiero fundición sobre el uso de as, a menos que sea válida para el objeto a ser un tipo diferente, que normalmente implica una verificación nula inmediatamente después. Así que aquí:

// What if foo is actually a Control, but we expect it to be String? 
string text = foo as string; 
// Several lines later 
int length = text.Length; // Bang! 

Aquí se conseguiría un NullReferenceException y, finalmente, lo cifran en text siendo nula - pero entonces no sabría si eso es porque foo era nula, o porque era un tipo inesperado. Si debe muy, muy ser un string, a continuación, fundido en su lugar:

string text = (string) foo; 

Ahora usted será capaz de notar la diferencia entre los dos escenarios.

En tercer lugar: como han dicho otros, valide sus datos, generalmente argumentos a API públicas y potencialmente internas. Hago esto en suficientes lugares en Noda Time que tengo una clase de utilidad para ayudarme a declutter el cheque. Así, por ejemplo (de Period):

internal LocalInstant AddTo(LocalInstant localInstant, 
          CalendarSystem calendar, int scalar) 
{ 
    Preconditions.CheckNotNull(calendar, "calendar"); 
    ... 
} 

Usted debe documento lo que puede y no puede ser nulo, también.

+0

sí, esos son buenos consejos. ¿Debería enviar los archivos pdb para que se entregue la línea? Actualmente mi código de producción proporciona el nombre del método pero no la línea que agrega un grado de dificultad. –

+0

Junto con "no hacer demasiado en cada línea" puede agregar "no hacer demasiado en cada método". Eso ayuda a minimizar el problema. – Servy

+0

@directedbit: depende de su situación ... para el código del lado del servidor, absolutamente. Si está enviando código del lado del cliente, ese * puede * ser más delicado o confuso ... –

2

Lamento decir que siempre realizaré una verificación para verificar que cualquier objeto que estoy usando en un método en particular no sea nulo.

Es tan simple como

if(this.SubObject == null) 
{ 
    throw new Exception("Could not perform METHOD - SubObject is null."); 
} 
else 
{ 
... 
} 

lo contrario, no se me ocurre ninguna manera de ser exhaustiva. No tendría mucho sentido para mí no hacer estos controles de todos modos; Siento que es solo una buena práctica.

+1

Espero que no solo arroje Exception ... –

+0

No siempre. Usualmente extiendo la excepción. Esto es solo un ejemplo. – DigitalJedi805

+2

En este caso, debe usar InvalidOperationException si el estado en sí mismo es válido, y ArgumentNullException para evitar que entre en ese estado si no es así. –

2

En primer lugar debe siempre validar sus entradas. Si null no está permitido, arroje un ArgumentNullException.

Ahora, sé cómo puede ser doloroso, por lo que podría buscar herramientas de reescritura de ensamblaje que lo hagan por usted.La idea es que usted tendría un tipo de atributo que marcaría los argumentos que no pueden ser null:

public void Method([NotNull] string name) { ... 

Y la re-escritura sería llenar los espacios en blanco ...

O una simple extensión método podría hacer más fácil

name.CheckNotNull(); 
+0

ese es un buen consejo. Desafortunadamente este era todo yo. Había perdido un corchete en la función por lo que una variable se estaba utilizando fuera del alcance de su inicialización. –

3

en muchos casos es casi imposible para planificar y dar cuenta de cada tipo de excepción que podría ocurrir en cualquier punto dado en el flujo de ejecución de la aplicación. La codificación defensiva es efectiva solo hasta cierto punto. El truco es tener una pila de diagnósticos sólida incorporada en su aplicación que pueda brindarle información significativa sobre los errores y bloqueos no controlados. Tener un buen controlador de nivel superior (último recurso) en el nivel del dominio de la aplicación ayudará mucho con eso.

Sí, enviar los PDB (incluso con una compilación de versión) es una buena forma de obtener un seguimiento completo de la pila que puede identificar la ubicación exacta y las causas de los errores. Pero cualquiera que sea el enfoque de diagnóstico que seleccione, debe integrarse en el diseño de la aplicación para comenzar (idealmente). Volver a adaptar una aplicación existente puede ser tedioso y requerir mucho tiempo o dinero.

+0

gracias, ¿hay alguna manera que recomiende para hornearlo? Actualmente tengo el enfoque simple de registrar la mayor cantidad de detalles posible para cualquier excepción no controlada. –

+0

Lo que pasa es que depende del método/ubicación particular ...a veces desea información específica sobre lo sucedido porque podría estar esperando ciertos tipos de errores. En otros, como dije, es posible que simplemente tenga una situación inesperada. Lo primero que puede hacer es conectar 'AppDomain.UnhandledException' (o' Application_Error' en global.asax para aplicaciones web), y luego comenzar a adaptar los métodos individuales según sea necesario. – kprobst

2

Si sólo están buscando una forma más compacta de código en contra de tener referencias nulas, no pase por alto el operador nulo coalescencia ??MSDN

Obviamente, depende de lo que está haciendo, pero se puede utilizar para evitar declaraciones extra if.

Cuestiones relacionadas