Digamos que tengo un método:
public void DoSomething(ISomeInterface someObject)
{
if(someObject == null) throw new ArgumentNullException("someObject");
someObject.DoThisOrThat();
}
Me han entrenado para creer que lanzar el mensaje ArgumentNullException
es "correcto" pero un error de "Referencia de objeto no establecido en una instancia de un objeto" significa que tengo un error.
¿Por qué?
Sé que si estaba almacenando la referencia en caché someObject
y la estaba usando más tarde, entonces es mejor verificar la nulidad cuando se pasa, y fallar temprano. Sin embargo, si estoy desreferenciando en la siguiente línea, ¿por qué se supone que debemos hacer la verificación? Va a lanzar una excepción de una manera u otra.
Editar :
Se me acaba de ocurrir ... ¿el miedo al nulo desreferenciado proviene de un lenguaje como C ++ que no lo comprueba por usted (es decir, solo intenta ejecutar algún método en la ubicación de memoria cero + desplazamiento del método)?
Respuestas:
Supongo que si está desreferenciando de inmediato la variable, podría debatir de cualquier manera, pero aún así preferiría ArgumentNullException.
Es mucho más explícito sobre lo que está sucediendo. La excepción contiene el nombre de la variable que era nula, mientras que una excepción NullReferenceException no. Particularmente en el nivel de API, una excepción ArgumentNullException deja en claro que algunas personas que llamaron no cumplieron con el contrato de un método, y la falla no fue necesariamente un error aleatorio o un problema más profundo (aunque ese puede ser el caso). Deberías fallar temprano.
Además, ¿qué es más probable que hagan los mantenedores posteriores? Agregue ArgumentNullException si agregan código antes de la primera desreferencia, o simplemente agregue el código y luego permita que se arroje una NullReferenceException?
fuente
Supongamos que llamo método
M(x)
que escribiste. Yo paso nulo. Recibo una excepción ArgumentNullException con el nombre establecido en "x". Esa excepción significa inequívocamente que tengo un error; No debería haber pasado nulo para x.Supongamos que llamo un método M que usted escribió. Yo paso nulo. Me sale una excepción nula deref. ¿Cómo diablos se supone que voy a saber si yo tengo un fallo, o que tienen un error o alguna biblioteca de terceros que se llama en mi nombre tiene un error? No sé nada sobre si necesito arreglar mi código o llamarlo para decirle que arregle el suyo.
Ambas excepciones son indicativas de errores; ni debe jamás ser lanzado en el código de producción. La pregunta es qué excepción comunica quién tiene el error mejor a la persona que realiza la depuración. La excepción nula deref no te dice casi nada; argumento nulo excepción te dice mucho. Sé amable con tus usuarios; lanzar excepciones que les permitan aprender de sus errores.
fuente
"neither should ever be thrown in production code"
¿Qué otros tipos de código / situaciones se usarían? ¿Deberían ser compilados comoDebug.Assert
? ¿O quieres decir que no los arrojes de una API?throw new ArgumentNullException
en su código de producción, y que nunca se debe correr al aire libre de un caso de prueba . La excepción nunca debería ser lanzada porque la persona que llama nunca debería tener ese error.El punto es que su código debe hacer algo significativo.
A
NullReferenceException
no es particularmente significativo, porque podría significar sobre todo. Sin mirar dónde se lanzó la excepción (y el código podría no estar disponible para mí) no puedo entender qué salió mal.Un
ArgumentNullException
es significativo, porque me dice: la operación falló porque el argumentosomeObject
eranull
. No necesito mirar tu código para resolverlo.También plantear esta excepción significa que usted maneja explícitamente
null
, aunque su única forma de hacerlo es decir que no puede manejarlo, mientras que solo permitir que el código se bloquee podría significar que, en realidad, podría manejar nulo , pero olvidé implementar el caso.El punto de excepciones es comunicar información en caso de falla, que se puede manejar en tiempo de ejecución para recuperarse de la falla, o en el momento de la depuración para comprender mejor qué salió mal.
fuente
Creo que su única ventaja es que puede proporcionar mejores diagnósticos en la excepción. Es decir, sabe que la persona que llama de la función está pasando nulo, mientras que si deja que la desreferencia la arroje, no estaría seguro de si la persona que llama pasa datos incorrectos o si hay un error en la función misma.
Lo haría si alguien más va a llamar a la función para que sea más fácil de usar (depurar si algo sale mal) y no hacerlo en mi código interno, porque el tiempo de ejecución lo comprobará de todos modos.
fuente
Tiene un error si el código no funciona según lo diseñado. El
NullReferenceException
sistema arroja un an y ese hecho realmente hace que sea más un error que una característica. No solo porque no definió la excepción específica que se debe manejar. También porque un desarrollador que lee su código puede suponer que no tuvo en cuenta la situación.Por razones similares,
ApplicationException
pertenece a su aplicación frente a un generalException
que pertenece al sistema operativo. El tipo de excepción que se lanza indica dónde se origina la excepción.Si ha contabilizado nulo, es mejor que lo maneje con gracia lanzando una excepción específica o si es una entrada aceptable, entonces no arroje una excepción porque son caros. Manejarlo realizando operaciones con buenos valores predeterminados o ninguna operación (por ejemplo, no se requiere actualización)
Finalmente, no descuides la capacidad de la persona que llama para manejar la situación. Al darles una excepción más específica,
ArgumentException
pueden construir su try / catch de tal manera que manejen este error antes que el más generalNullReferenceException
que puede ser causado por cualquier otra razón que ocurra en otros lugares, como la falla al inicializar una variable de constructor.fuente
La verificación hace más explícito lo que está sucediendo, pero el verdadero problema viene con un código como este (artificial) ejemplo:
Aquí hay una cadena de llamadas involucrada y sin verificación nula, solo ve el error en algún otro objeto y no donde se reveló el problema por primera vez, lo que significa instantáneamente pérdida de tiempo en la depuración o interpretación de las pilas de llamadas.
En general, es mejor ser coherente con este tipo de cosas y siempre agregar el cheque nulo, lo que hace que sea más fácil detectar si falta uno en lugar de elegir y elegir caso por caso (suponiendo que sepa que nulo es siempre inválido).
fuente
Otra razón a tener en cuenta es ¿qué sucede si se produce algún procesamiento antes de usar el objeto nulo?
Supongamos que tiene un archivo de datos de ID de empresa asociada (Cid) e ID de persona (Pid). Se guarda como una secuencia de pares de números:
Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid
Y esta función VB escribe los registros en el archivo:
Si
Person
es una referencia nula, la líneaFile.Write(Person.ID)
arrojará una excepción. El software puede detectar la excepción y recuperarse, pero esto deja un problema. Se ha escrito una identificación de la empresa sin una identificación de persona asociada. De hecho, la próxima vez que llame a esta función, colocará una identificación de la compañía donde se esperaba la identificación de la persona anterior. Además de que ese registro es incorrecto, cada registro posterior tendrá una identificación de persona seguida de una identificación de la empresa, que es al revés.Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid
Al validar los parámetros al comienzo de la función, evita que se produzca cualquier procesamiento cuando se garantiza que el método fallará.
fuente