32 votes

Impossible NullReferenceException?

Je recherche une exception qu'un collègue viens de recevoir lors de l'exécution d'une application par le biais 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) 

À l'aide de .NET Réflecteur, j'ai regardé le code pour
GenericEqualityComparer<T>.Equals(T x, T y), et je ne vois aucune cause possible pour un 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;
}

Le type d' T, TKey et TIdentity sont tous du même type dans cette trace de la pile.

Le type est un type personnalisé appelé Identity qui implémente IEquatable<Identity>. Il est immuable et ne peut pas être construit avec des valeurs null dans les champs qu'il utilise dans sa mise en œuvre d' Equals(Identity other). Il remplace également Equals(object obj) comme ceci:

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);
}

J'ai un assez exhaustive de tests unitaires autour de l' Equals des implémentations. Donc, il se fera un plaisir d'accepter une valeur nulle pour les autres/obj et retournera false comme prévu.

Le type n'a pas supplanter l' == opérateurs ni != opérateurs.

Même ainsi, je m'attends à voir ma classe sur le dessus de la trace de la pile si l'exception a été levée à partir de la mise en œuvre de l' Equals(Identity other) mon Identity classe, mais il est dit que l' NullReferenceException provient mscorlib.

Je suis en cours d'exécution sur .NET Framework version 4.0.30319.269.

Je n'ai pas de vidage de la mémoire, et je n'ai pas vu ça avant, et n'ont pas reproduit depuis. Pourtant, je suis obligé d'enquêter et d'être absolument certain qu'il n'est pas provoqué par notre code et qu'il n'arrivera pas dans la production.

Donc, la vraie question est: Quelle est la cause de cette exception?

  • Bug dans mscorlib (semble très peu probable)
  • Transitoire de corruption de mémoire sur la machine (ce qui est possible, difficile de revenir avec preuve)
  • Les autres?

* Mises à jour en réponse à Jordão *

Est-il possible d'appeler la méthode avec un objet qui n'est pas une Identité?

L' ConcurrentDictionary<TKey, TValue> est tapé tels que TKey = Identity et rien ne les sous-classes Identity. Donc, je ne vois pas comment cela pourrait être possible.

Est-il possible d'appeler la méthode avec null?

Les tests unitaires couvrir le scénario de l'appel de l'ensemble de l' Equals des implémentations avec la valeur null.

Quelle est la version du code est la trace de la pile d'? Peut-être que certains anciens de la version sensibles à l'exception?

Je suis en analysant le même code qui a généré une exception. J'ai vérifié que la version de l' .NET Framework en cours d'exécution sur mes collègues de l'ordinateur est également 4.0.30319.269.

Tout multithread scénario pourrait provoquer l'exception? Elles sont généralement difficiles à reproduire, mais peut être utile de l'examiner.

Oui, le code est multi-thread et destiné à être. Donc, c'est pourquoi je suis à l'aide d'un ConcurrentDictionary.

* Suivi liées à la réponse de Jalal Aldeen Asa avait *

J'aurais pensé qu'une situation de concurrence où à un autre thread ensembles x de null ne pouvait être la cause, si le paramètre x a été adoptée par référence à l'aide de la " ref " mot-clé. Je me mis à valider cette théorie avec le code suivant:

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;
}

et le test se termine sans erreur.

Je peux forcer ce comportement si je change la signature de passer x et y par référence (c'est - public bool Equals<T>(ref T x, ref T y) then the test fails with aNullReferenceException, but this does not match the method signature ofGenericEqualityComparer.Est égal à(T x, T y)`.

4voto

MPelletier Points 8326

Je vais exposer mon hypothèse ici.

La pile vous porte à croire que c'est là que se produit le crash, mais cela se produit ailleurs. Nous examinons le mauvais fil.

Je ne sais pas si cela serait pratique, mais parfois, le bon vieux "débogage printf" aide. Que faire si vous imprimez la valeur que vous recherchez avant d'appeler TryGetValue ? Vous verriez si vous annulez ou non.

1voto

Ken Beckett Points 577

Je suis tombé sur une référence nulle exception dans Équivaut à environ un il ya quelques années (pas sûr si c'était de 3.5 ou 4.0, ou s'il a jamais été fixe). Il n'est pas clair pour moi ce que les types sont comparés dans votre cas, mais dans ma situation, il se produit au moment où la comparaison d'une MethodInfo réflexion pour objet une méthode générique de la déclaration de non-MethodInfo objet... Ka-boom! Donc, si vous êtes en comparant la réflexion des objets, ce pourrait être elle. Si vous ne l'êtes pas, eh bien au moins, je peux témoigner du fait qu'il y est au moins un est Égal à la mise en œuvre de la BCL qui peut renvoyer null exceptions de référence pour aucune bonne raison, dans certaines situations, donc il peut très bien y en avoir d'autres. Même le sacré .NET BCL est encore les logiciels, et TOUS les logiciels ont des bugs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X