¿Cómo ayuda lanzar una excepción ArgumentNullException?

27

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 ArgumentNullExceptiones "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é someObjecty 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)?

Scott Whitlock
fuente
Hacer explícita la excepción ayuda a afirmar que existe la posibilidad de error, lo que se puede observar directamente en la documentación.
zzzzBov

Respuestas:

34

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?

Matt H
fuente
Por extensión, si este fuera un método no público, o un método público en una clase no pública, ¿no cree que hay una buena razón para la verificación nula?
Scott Whitlock
Por lo general, no agrego estos controles a métodos privados, pero aún puedo agregarlos a métodos públicos de clases privadas para que sean coherentes. Para los métodos privados, tiendo a suponer que los argumentos se validan antes en algún nivel de llamada pública.
Matt H
54

Me han entrenado para creer que lanzar la excepción ArgumentNullException es "correcto", pero un error "Referencia de objeto no establecido en una instancia de un objeto" significa que tengo un error. ¿Por qué?

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.

Eric Lippert
fuente
No estoy seguro de entender "neither should ever be thrown in production code"¿Qué otros tipos de código / situaciones se usarían? ¿Deberían ser compilados como Debug.Assert? ¿O quieres decir que no los arrojes de una API?
StuperUser
66
@StuperUser: Creo que entendiste mal lo que quise decir, porque lo escribí de manera confusa. Usted debe tener throw new ArgumentNullExceptionen 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.
Eric Lippert
1
No solo comunica quién tiene el error, también comunica dónde está el error. Incluso si ambas áreas son mías, no siempre es fácil entender exactamente qué variable era nula.
Ohad Schneider
5

El punto es que su código debe hacer algo significativo.

A NullReferenceExceptionno 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 ArgumentNullExceptiones significativo, porque me dice: la operación falló porque el argumento someObjectera null. 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.

back2dos
fuente
2

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.

Jan Hudec
fuente
1

Me han entrenado para creer que lanzar la excepción ArgumentNullException es "correcto", pero un error "Referencia de objeto no establecido en una instancia de un objeto" significa que tengo un error.

Tiene un error si el código no funciona según lo diseñado. El NullReferenceExceptionsistema 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, ApplicationExceptionpertenece a su aplicación frente a un general Exceptionque 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, ArgumentExceptionpueden construir su try / catch de tal manera que manejen este error antes que el más general NullReferenceExceptionque puede ser causado por cualquier otra razón que ocurra en otros lugares, como la falla al inicializar una variable de constructor.

P.Brian.Mackey
fuente
1

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:

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

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).

FinnNk
fuente
1

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:

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

Si Persones una referencia nula, la línea File.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á.

Hand-E-Food
fuente