2012-05-20 10 views
10

¿Cuándo debo protegerme contra los argumentos null? Idealmente, protegería contra null en todas partes, pero eso se vuelve muy hinchado y tedioso. También observo que las personas no están poniendo guardias en cosas como AsyncCallback s.¿Cuándo debo protegerme contra el nulo?

Para no molestar a otras personas con muchos códigos unidiomáticos, ¿hay alguna norma aceptada sobre dónde debo protegerme contra null?

Gracias.

+6

Los pongo en todas partes donde considero 'null' como un argumento inválido. –

+1

Puede que le interese esta pregunta en el sitio hermano de los programadores: [¿Debería uno verificar nulo si no espera nulo?] (Http://programmers.stackexchange.com/questions/147480/should-one-check-for -null-if-he-does-not-expect-null/147483) – R0MANARMY

+0

No parece ser un estándar. ¿Sería apropiado que alguien hiciera la pregunta wiki de la comunidad? –

Respuesta

5

Un método que he utilizado mucho es la null object pattern. Por ejemplo, si una tiene una clase de fábrica que devuelve diferentes implementaciones de una interfaz basada en un argumento y el argumento proporcionado no está asignado a ninguna de las implementaciones, Yo devolvería un objeto Null, , por ej.

public interface IFoo{ 
     void Bar(); 
    } 
    public class NullFoo{ 
     public void Bar(){ 
      //null behaviour 
     } 
    } 
    public class FooFactory{ 
     public IFoo CreateFoo(int i){ 
       switch(i){ 
        case 1: 
        return new OneFoo(); 
        break; 
        case 2: 
        return new TwoFoo(); 
        break; 
        default: 
        return new NullFoo(); 
        break; 
       } 
     } 
    } 

Cuando quiero obtener una IFoo de CreateFoo, no tengo para comprobar si el objeto devuelto es nulo.

Obviamente, este es solo uno de los muchos enfoques. No existe una "talla única", ya que null puede significar cosas diferentes.

Otra forma de protegerse de los argumentos nulos es usar CodeContract preconditions. p.

public void Foo(Bar x){ 
     Contract.Requires<ArgumentNullException>(x != null, "x"); 
     //access x 
    } 

uso de contratos de código le permite ejecutar análisis de código estático contra su código y captura insectos tales como Foo(null). (more here)

Uno más por qué hacerlo sería utilizar un método simple extensión genérica:

public static class Ex 
{ 
    public static void EnsureNotNull<T>(this T t, string argName) where T:class 
    { 
     if(t == null) 
     { 
      throw new ArgumentNullException(argName); 
     } 
    } 
} 

entonces usted puede comprobar sus argumentos así:

public void Foo(Bar x, Bar y){ 
    x.EnsureNotNull("x"); 
    y.EnsureNotNull("y"); 
} 
+1

El patrón de objeto nulo solo tiene sentido en algunos casos, solo aquellos en los que no es esencial para su objeto hacer algo realmente. Fallar ruidosamente y fracasar temprano es definitivamente preferible en la mayoría de las situaciones. Todavía tendrá que comprobar explícitamente null en los casos en que acepte un argumento del código de usuario a menos que pueda estar absolutamente seguro de que el valor pasado proviene de un valor de retorno de su API.Gran patrón cuando sea apropiado, aunque :) –

+0

+1 para una buena información. No creo que esto resuelva toda la cuestión, pero sigue siendo información muy útil. –

+0

En su ejemplo de código, la clase 'NullFoo' debe implementar la interfaz' IFoo'. Traté de editar la respuesta para solucionarlo, pero mi edición fue rechazada. – polkduran

1

La comprobación nula de API públicas es imprescindible. Es más fácil (y más seguro) para los desarrolladores si saben por error que tienen un error debido a un parámetro nulo en lugar de intentar depurar alguna otra excepción oscura como resultado de ese parámetro nulo.

Internamente, los constructores son un buen lugar para buscar nulos por la misma razón: es más fácil atraparlo en el constructor que cuando se llama a un método en la clase.

Algunos argumentan que es bueno comprobar en todas partes, pero tiendo a pensar que hace más código para examinar cuando se lee el código.

1

veces hago una clase Preconditions que contiene métodos estáticos para el control de algunas condiciones previas método común, es decir, un argumento null - algo como esto por ejemplo:

public static <T> T checkNull(T arg) { 
    if (arg == null) 
     throw new IllegalArgumentException(); 
    return arg; 
} 

Esto mantiene su código un poco más limpio.

En cuanto a donde debe comprobar null, debe hacerse donde sea que no sea un valor válido.

Editar

notado esta pregunta fue etiquetado como C#, pero se entiende la idea ...

+0

Nitpick: Prefiero arrojar una 'ArgumentNullException'. –

+0

@Cicada: Buena llamada. –

1

Si estás hablando de argumentos del método, usted tiene una opción. Puede verificar los argumentos y arrojar ArgumentNullException, o puede ignorar el cheque y tener algo más arrojar NullReferenceException más adelante en la línea. El riesgo que corre al no verificar los argumentos es que su código puede cambiar algún estado global antes de que se genere el NullReferenceException, dejando el programa en un estado desconocido.

En general, es probablemente mejor verificar los argumentos y documentar que su método arroja ArgumentNullException. Sí, es un poco más de trabajo y puede ser molesto pasar por esos controles al comienzo de un método. Debe preguntarse si el código más sólido que obtiene a cambio es una buena compensación.

Consulte This link para una buena discusión sobre este tema.

+1

Otra buena razón para verificar argumentos es esta: cualquier excepción que pueda ocurrir más tarde, posiblemente varias llamadas en la pila de llamadas, puede no ayudar al desarrollador que está llamando a su código. No quedará claro si esa excepción fue el resultado de un argumento nulo para su método, o de un error en * su * código. Fallar rapido. – phoog

3

Cuando ¿Debería protegerme de argumentos nulos?

Supongo que está hablando de argumentos nulos pasados ​​a métodos públicos o constructores de código que ha escrito. Tenga en cuenta que también es posible que tenga que "proteger" contra null cada vez que llame a una dependencia externa que pueda devolver nulo, a menos que su código pueda manejar esos casos con elegancia.

Debe evitar el valor nulo en cualquier método público (incluidos los constructores y los establecedores de propiedades) que expone a un usuario donde el valor nulo no tiene un significado útil y explícito. Si un valor nulo no significa algo especial para su código (por ejemplo, final de la matriz, "desconocido", etc.), entonces no debe aceptar ese valor, y debería arrojar un ArgumentNullException en su lugar.

Esta regla no es exclusiva de null tampoco. Siempre debe verificar los argumentos pasados ​​a sus métodos públicos.

Por ejemplo, supongamos que está escribiendo algún tipo de método de servicio web que toma un ID de usuario y le hace algo a un usuario (por ejemplo, eliminarlos). Debes verificar que sea un ID de usuario válido antes de que tu método haga algo más con ese usuario. Otro ejemplo es si está escribiendo un método público que lleva un índice a una colección o matriz. Debe comprobar de antemano que el índice está dentro del rango permitido, que el índice no es más grande que la colección, o menor que cero.

También observo que las personas no están poniendo guardias en cosas como AsyncCallbacks.

Si usted sabe que su método de pre-condiciones se mantienen vigilantes por los argumentos que se pasan a los métodos (porque usted lanza excepciones si no lo son), entonces es libre para saltar estos controles en su privada e interna métodos, o clases privadas e internas.

Pero como ha señalado, debe tener cuidado de no confiar en ningún valor de retorno de una API que no haya escrito, o de que haya pasado algún valor en sus métodos de devolución de llamada. Trátelos como "sucios" y suponga que pueden ser valores nulos o no válidos.

que se pone muy hinchado y tediosa

Especificar y hacer el seguimiento de las condiciones previas para sus métodos públicos no es la hinchazón - se compila documentación. Es la forma de asegurarse de que su código es derecho. Así es como se les dice a los usuarios de su código por adelantado que hicieron algo mal. Permitir que esto falle en el medio de su método (o tal vez en otro método en alguna clase vagamente relacionada) hace que sea mucho más difícil depurar su problema.

Esto puede no parecer un gran problema ahora. Pero una vez que comienza a recibir las quejas de los clientes con un NullReferenceException 5 niveles por debajo de su pila, 20 llamadas a métodos más tarde, entonces creo que usted comenzará a ver los beneficios :)

Para evitar molestas otras personas con mucho de código unidiomático, ¿hay alguna norma aceptada en cuanto a dónde debo protegerme contra nulo?

Normalmente la gente escribe el código if ... throw en la parte superior de su método. Esta es la sintaxis más idiomática, y muy fácil de entender incluso para principiantes. Más o menos parecidos a los parens en Lisp, una vez que se utiliza ese patrón, se puede rozar muy rápidamente, sin pensarlo.

Puede hacer que escribir estos controles sea más rápido usando Visual Studio Code Snippets.

Puede acortar un poco este código utilizando o creando algún código compartido que admita la sintaxis de aserción. En lugar de la sintaxis if ... throw, escribiría una línea como Assert.NotNull(arg1, "arg1");. Si desea algo de inspiración, puede consultar el NUnit del marco assertions y constraints.

Es posible que también desee consultar el Code Contracts API. Está diseñado para verificar condiciones previas, condiciones posteriores e invariantes (que son los nombres formales de estas "condiciones de guardia"). También puede mover parte de esta verificación del tiempo de ejecución al tiempo de compilación, para que pueda descubrir que ha cometido errores incluso antes de ejecutar su programa. Realmente no lo he visto, pero también podría darte una sintaxis más concisa. Editar: También vea Pencho Ilchev's answer para ver un pequeño ejemplo del uso de parte de esta API.

Cuestiones relacionadas