validar tipo objeto not net excepción error controlada como asp c# nullreferenceexception mscorlib

c# - tipo - system nullreferenceexception como resolver



¿Imposible NullReferenceException? (2)

Estoy investigando una excepción que un colega acaba de obtener al ejecutar una aplicación a través de Visual Studio 2010:

System.NullReferenceException was unhandled by user code Message=Object reference not set to an instance of an object. Source=mscorlib StackTrace: at System.Collections.Generic.GenericEqualityComparer`1.Equals(T x, T y) at System.Collections.Concurrent.ConcurrentDictionary`2.TryGetValue(TKey key, TValue& value) at xxxxxxx.xxxxxxx.xxxxxxx.RepositoryBase`2.GetFromCache(TIdentity id)

Usando .NET Reflector , miré el código para
GenericEqualityComparer<T>.Equals(T x, T y) , y no puedo ver ninguna causa posible para una NullReferenceException .

//GenericEqualityComparer<T>.Equals(T x, T y) from mscorlib 4.0.30319.269 public override bool Equals(T x, T y) { if (x != null) { return ((y != null) && x.Equals(y)); } if (y != null) { return false; } return true; }

El tipo de T, TKey y TIdentity son todos del mismo tipo en este seguimiento de pila .

El tipo es un tipo personalizado denominado Identity que implementa IEquatable<Identity> . Es inmutable y no se puede construir con valores nulos para los campos que usa en su implementación de Equals(Identity other) . También anula Equals(object obj) como este:

public override bool Equals(object obj) { if ((object)this == obj) { return true; } return Equals(obj as Identity); } public bool Equals(Identity other) { if ((object)this == (object)other) { return true; } if ((object)other == null) { return false; } if (!FieldA.Equals(other.FieldA)) { return false; } return FieldB.Equals(other.FieldB); }

Tengo un conjunto bastante exhaustivo de pruebas unitarias en torno a las implementaciones de Equals . Por lo tanto, aceptará felizmente un valor de nulo para other / obj y devolverá falso como se esperaba.

El tipo no anula los operadores == ni los operadores != .

Aun así, esperaría ver a mi clase en la parte superior del seguimiento de la pila si se lanzara la excepción desde la implementación de Equals(Identity other) en mi clase Identity , pero dice que NullReferenceException proviene de mscorlib .

Me estoy ejecutando en .NET Framework versión 4.0.30319.269.

No tengo un volcado de memoria, y no he visto esto antes y no lo he reproducido desde entonces. Aún así, estoy obligado a investigar y estar absolutamente seguro de que no está siendo causado por nuestro código y que no ocurrirá en producción.

Entonces, la verdadera pregunta es: ¿qué causó esta excepción?

  • Error en mscorlib (parece altamente improbable)
  • Corrupción de memoria transitoria en la máquina (posible, difícil de respaldar con evidencia)
  • ¿Otro?

* Actualizaciones en respuesta a Jordão *

¿Es posible llamar al método con un objeto que no es una identidad?

El ConcurrentDictionary<TKey, TValue> se escribe de manera que TKey = Identity y nada subclases Identity . Entonces, no puedo ver cómo podría ser posible.

¿Es posible llamar al método con nulo?

Las pruebas unitarias cubren el escenario de llamar a todas las implementaciones de Equals con nulo.

¿De qué versión del código es el seguimiento de la pila? Tal vez alguna versión anterior susceptible a la excepción?

Estoy analizando el mismo código que generó la excepción. He comprobado que la versión de .NET Framework que se ejecuta en la computadora de mis colegas también es 4.0.30319.269.

Cualquier escenario multiproceso podría causar la excepción? Estos suelen ser difíciles de reproducir, pero podría valer la pena investigar.

Sí, el código es multihilo y está destinado a ser. Entonces, es por eso que estoy usando un ConcurrentDictionary .

* Seguimiento relacionado con la respuesta de Jalal Aldeen Saa''d *

Hubiera pensado que una condición de carrera donde otro hilo establezca x null solo podría ser la causa si el parámetro x se pasara por referencia usando la palabra clave ''ref''. Me propuse validar esa teoría con el siguiente código:

ManualResetEvent TestForNull = new ManualResetEvent(false); ManualResetEvent SetToNull = new ManualResetEvent(false); [TestMethod] public void Test() { var x = new object(); var y = new object(); var t = Task.Factory.StartNew(() => { return Equals(x, y); }); TestForNull.WaitOne(); //wait until x has been tested for null value x = null; SetToNull.Set(); //signal that x has now been set to null var result = t.Result; Assert.IsFalse(result); } public bool Equals<T>(T x, T y) { if (x != null) { TestForNull.Set(); //signal that we have determined that x was not null SetToNull.WaitOne(); //wait for original x value to be set to null //would fail here if setting the outer scope x to null affected //the value of x in this scope return ((y != null) && x.Equals(y)); } if (y != null) { return false; } return true; }

y la prueba se completa sin errores.

Puedo forzar ese comportamiento si cambio la firma para pasar y por referencia (es decir, public bool Equals<T>(ref T x, ref T y) then the test fails with a NullReferenceException , but this does not match the method signature of GenericEqualityComparer.Equals (T x, T y) `.


Me encontré con una excepción de referencia nula en Equals hace unos años (no estoy seguro si estaba en 3.5 o 4.0, o si alguna vez fue corregido). No tengo claro qué tipos se comparan en su caso, pero en mi situación ocurriría siempre que comparase un objeto de reflexión MethodInfo para una declaración de método genérico con CUALQUIER objeto no MethodInfo ... ¡Ka-boom! Entonces, si estás comparando objetos de reflexión, podría ser. Si no lo eres, al menos puedo dar fe del hecho de que hay al menos una implementación Equals en el BCL que puede arrojar excepciones de referencia nula sin una buena razón en ciertas situaciones, por lo que podría haber otras. Incluso el .NET BCL sagrado sigue siendo software, y TODO el software tiene errores.


Voy a exponer mi hipótesis aquí.

La pila te está llevando a creer que es aquí donde se produce el bloqueo, pero ocurre en otro lado. Estamos viendo el hilo equivocado.

No sé si esto sería práctico, pero a veces ayuda la buena "depuración de errores de impresión". ¿Qué TryGetValue si imprime el valor que está buscando antes de llamar a TryGetValue ? Vería si golpea un nulo o no.