2011-08-08 20 views
6

Lo que más me molesta acerca de algunos lenguajes de programación (por ejemplo, C#, Javascript) es que al intentar acceder a una propiedad de null se produce un error o una excepción.¿Por qué intentar acceder a una propiedad de null causa una excepción en algunos idiomas?

Por ejemplo, en el siguiente fragmento de código,

foo = bar.baz; 

si la barra es null, C# lanzará una desagradable NullReferenceException y mi intérprete Javascript se quejará con Unable to get value of the property 'baz': object is null or undefined.

puedo entender esto, en teoría, pero en el código real que a menudo tienen poco profundas objetos, como

foo.bar.baz.qux 

y si alguno de entre foo, bar, o baz es nulo, mis códigos se rompen. :(Además, si evalúo las siguientes expresiones en una consola, parece que hay inconsistencias en los resultados:.

true.toString() //evaluates to "true" 
false.toString() //evaluates to "false" 
null.toString() //should evaluate to "null", but interpreter spits in your face instead 

I desprecian absolutamente escribir código para manejar este problema, porque siempre es prolijo, código hediondos La siguientes son ejemplos no artificiales, agarré estos de uno de mis proyectos (el primero es en Javascript, el segundo es en C#):

if (!(solvedPuzzles && 
     solvedPuzzles[difficulty] && 
     solvedPuzzles[difficulty][index])) { 
     return undefined; 
    } 
return solvedPuzzles[difficulty][index].star 

y

if (context != null && 
    context.Request != null && 
    context.Request.Cookies != null && 
    context.Request.Cookies["SessionID"] != null) 
{ 
    SessionID = context.Request.Cookies["SessionID"].Value; 
} 
else 
{ 
    SessionID = null; 
} 

Las cosas serían mucho más fáciles si toda la expresión devolviera null si alguna de las propiedades fuera nula. Los ejemplos de código anteriores podrían haber sido mucho más simples:

return solvedPuzzles[difficulty][index].star; 
    //Will return null if solvedPuzzles, difficulty, index, or star is null. 

SessionID = context.Request.Cookies["SessionID"].Value; 
    //SessionID will be null if the context, Request, Cookies, 
    //Cookies["SessionID"], or Value is null. 

¿Hay algo que me falta? ¿Por qué estos idiomas no usan este comportamiento en su lugar? ¿Es difícil de implementar por alguna razón? ¿Causaría problemas que estoy pasando por alto?

+0

¿Es nulo un valor válido para todos esos valores en la cadena? Es más fácil simplemente no almacenar nulos allí en primer lugar. Entonces puedes evitar esos cheques por completo. –

+1

Te va a encantar el [patrón de objeto nulo] (http://en.wikipedia.org/wiki/Null_Object_pattern) ... – fredoverflow

+3

No es culpa del idioma que decidió tener una colección nula en lugar de una colección vacía .. – canon

Respuesta

8

¿Causaría problemas que estoy pasando por alto?

Sí - que podría causar el problema en el que espera que haya valores no nulos, pero debido a un error, usted tiene un valor nulo. En esa situación desea una excepción. Fallar silenciosamente y seguir con malos datos es una muy mala idea.

Algunos idiomas (como Groovy) proporcionan un operador de desreferencia seguro de nulos que puede ayudar. En C# podría ser algo como:

SessionID = context?.Request?.Cookies?["SessionID"]?.Value; 

Creo que el equipo de C# han considerado esto en el pasado y lo encontró problemático, pero eso no quiere decir que no se volver en el futuro, por supuesto, .

2

Y luego, si el final de eso fue una llamada a un método que devolvió bool, ¿entonces qué? Main.Child.Grandchild.IsEdit() y bool IsEdit();

creo que sería mejor tener una instancia de "nulo" que devuelve el comportamiento por defecto (esto se conoce como the null object pattern).

De esta manera, para otros que esperan que un nulo indique un problema, obtienen su excepción, pero si usted sabe que un objeto predeterminado es aceptable, puede implementarlo y no preocuparse por ello. Ambos casos se resuelven.

Puede derivar todos los objetos "nulos" de INull, ponerlos en un hash en contra de su nombre de clase, y luego referirse a eso si un miembro es nulo. Entonces puede controlar la implementación "nula" predeterminada (lo que sería aceptable si el objeto es "nulo")

Además, los objetos "nulos" pueden arrojar excepciones si se accede, de esa manera usted sabe que accedió a uno, y elige cuando esto estaría bien. Para que pueda implementar su propia "excepción de objeto nulo".

Si ponen cualquier soporte de idioma para esto, sería para todos, en general para la unidad de traducción u objeto por objeto con un calificador (la última opción es la preferida), en ese punto no lo haría t por defecto, y ya podría haber implementado una instancia "nula" predeterminada.

+0

Bueno ... ¿Hacerlo 'bool? IsEdit() ': P –

+0

No sé si eso es lo mejor porque no creo que tengas el mismo control. –

+0

Ciertamente no es mejor. Estaba bromeando. –

1

Podría sugerir que si tiene que comprobar tantos nulos, está estructurando su código deficientemente. Obviamente, como no tengo tu código, no puedo decirlo de manera concluyente. Sugeriría que dividas tu código en funciones más pequeñas. De esta forma, su código solo tiene que verificar uno o quizás dos valores nulos a la vez, y puede tener más control sobre lo que sucede si en algún momento algo es nulo.

+0

Tengo que estar de acuerdo con esto: sus invariantes no son lo suficientemente fuertes. – Puppy

0

En general foo.Baz significa "en la instancia si foo, ejecuta el miembro baz". Esto podría ser un lenguaje dinámico, un miembro virtual, o cualquier cosa, pero hemos fallado en el primer paso en la premisa: no hay instancia.

Lo que está afilado es un operador de acceso de miembro nulo seguro. Bastante raro, y francamente estoy en desacuerdo con su afirmación de que esto hace que el código huele mal (aunque podría argumentar que está consultando demasiados niveles en una sola expresión para estar sano).

Del mismo modo, no estoy de acuerdo con que null.toString() devuelva "nulo" - IMO es perfecto y normal para que esto falle. Tratar el acceso en un nulo como "normal" es inherentemente incorrecto.

+0

Para los tipos que se supone que representan entidades o titulares de datos mutables, las operaciones en 'null' no tienen realmente sentido. Para tipos de referencia inmutables, sin embargo, lo único que sería "incorrecto" al permitir que los tipos hicieran 'default (someImmutableRefType) .SomeMember()' haría algo útil sería el hecho de que la restricción 'struct' en' Nullable ' significa que no habría una forma "estándar" para definir un equivalente con nulo de ese tipo. De lo contrario, no veo ninguna razón para que un tipo de referencia inmutable no pueda funcionar como un tipo de valor, incluido tener un valor predeterminado significativo. – supercat

0

¿Hay algo que me falta? ¿Por qué estos idiomas no usan este comportamiento en el ? ¿Es difícil de implementar por alguna razón? ¿Sería causar problemas que estoy pasando por alto?

Cuando una variable es null cuando no se espera que el idioma que mencionas elegir a falla temprana. Este es un concepto central al crear programas que funcionan correctamente.

La opción sería ocultar el problema del puntero nulo y el programa ejecutaría una fila más, pero probablemente se bloquee más tarde o, incluso peor, provoque una salida incorrecta.

También cuando se trata de reparar un error, es mucho más fácil repararlo si arroja un NullReferenceException de inmediato y el depurador muestra la línea para comenzar a mirar. La alternativa sería síntomas vagos: ¡mucho más difícil de depurar!

Es muy común programar con null cuando no debería.En los ejemplos anteriores, ¿qué significa null. La respuesta es que no significa nada. Como lector del código, supongo que el escritor olvidó algo.

Lo que generalmente es la mejor solución es introducir un pequeño, lo que yo llamo, "objeto nulo" que se puede devolver en lugar de null. En su ejemplo, tal vez una clase "NoDiffficulty" o "UnknownDiffficulty" podría ser devuelta para arrojar el resultado que busca al final: limpiar el código.

+0

Honestamente 'NullReferenceException' no está lejos de ser un síntoma vago, porque la línea' x = null' podría estar a cientos de millas de la línea 'y.foo()' que arroja. –

+0

sí, lo es. Confía en mí, he intentado ambos. :) – vidstige

Cuestiones relacionadas