La référence d'objet n'est pas définie sur une instance d'un objet. Pourquoi .NET n'indique-t-il pas quel objet est `null '?

103

Concernant ce message d'exception non gérée .NET:

La référence d'objet n'est pas définie à une instance d'un objet.

Pourquoi .NET ne montre-t-il pas quel objet est null?

Je sais que je peux rechercher nullet résoudre l'erreur. Cependant, pourquoi .NET n'aide-t-il pas à indiquer quel objet a une référence null et quelle expression a déclenché le NullReferenceException?


la source
2
Lorsque cela se produit, réécrivez la ligne sur laquelle il s'est produit afin qu'il vérifie d'abord la valeur nulle de chaque résultat possible - vous saurez alors exactement ce que c'était. Soit cela, soit le débogueur incroyable de Visual Studio est attaché, ce qui interrompt l'instant où une exception se produit et vous permet de voir ce qui est nul :)
Patashu
5
Pas vraiment, il demande simplement pourquoi le framework .NET n'aide pas le programmeur à montrer quel objet est nul. Je suppose que c'est la pénalité des performances (vous auriez besoin de réflexion). mais je ne suis pas sûr non plus.
bas
1
@bas: Bien que cela soit vrai, la question est un peu trompeuse dans la mesure où elle devrait concerner une "partie d'une expression", pas un "objet". Cela explique également pourquoi une simple réflexion n'aidera pas, mais des informations de débogage détaillées seront nécessaires.
OR Mapper
4
Je suis toujours curieux de connaître la réponse. C'est un peu similaire aux exceptions .net qui n'aident pas à indiquer quelle clé n'existe pas dans un dictionnaire. De plus, je ne comprends pas les dévotes sur la question.
bas
12
Terminologie svp: Un objet n'est jamais nul. Une référence d'objet pourrait être cependant. Mais une référence d'objet n'est qu'un emplacement en mémoire - comment cela vous aiderait-il, à moins que vous n'ayez un débogueur attaché de toute façon?
Oskar Berggren

Réponses:

169

(Pour plus d'informations sur le nouvel assistant d'exception dans Visual Studio 2017, consultez la fin de cette réponse)


Considérez ce code:

String s = null;
Console.WriteLine(s.Length);

Cela lèvera un NullReferenceExceptiondans la deuxième ligne et vous voulez savoir pourquoi .NET ne vous dit pas squ'il était nul lorsque l'exception a été levée.

Pour comprendre pourquoi vous n'obtenez pas cette information, vous devez vous rappeler que ce n'est pas la source C # qui s'exécute mais plutôt IL:

IL_0001: ldnull      
IL_0002: stloc.0 // s
IL_0003: ldloc.0 // s
IL_0004: callvirt System.String.get_Length
IL_0009: appelez System.Console.WriteLine

C'est l' callvirtopcode qui lance le NullReferenceExceptionet il le fait lorsque le premier argument de la pile d'évaluation est une référence nulle (celle qui a été chargée avec ldloc.0).

Si .NET doit être capable de dire qu'il ss'agit d'une référence nulle, il doit d'une certaine manière suivre que le premier argument de la pile d'évaluation est issu de la forme s. Dans ce cas, il est facile pour nous de voir que c'est snul, mais que se passe-t-il si la valeur est une valeur de retour d'un autre appel de fonction et n'est stockée dans aucune variable? Quoi qu'il en soit, ce type d'informations n'est pas ce que vous voulez garder une trace dans une machine virtuelle comme la machine virtuelle .NET.


Pour éviter ce problème, je vous suggère d'effectuer une vérification d'argument null dans tous les appels de méthode publique (à moins bien sûr que vous n'autorisiez la référence null):

public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

Si null est passé à la méthode, vous obtenez une exception qui décrit précisément le problème (c'est-à- sdire null).


Quatre ans plus tard, Visual Studio 2017 dispose désormais d'un nouvel assistant d'exception qui essaiera de dire ce qui est nul quand a NullReferenceExceptionest levé. Il est même capable de vous donner les informations requises lorsque c'est la valeur de retour d'une méthode qui est nulle:

Aide aux exceptions Visual Studio 2017

Notez que cela ne fonctionne que dans une version DEBUG.

Martin Liversage
la source
5
Les numéros de ligne et les noms de fichier source ne sont pas non plus stockés dans le code IL lui-même, ou le sont-ils? Pourtant, ils peuvent être mis à disposition pour le débogage.
OR Mapper
4
@MartinLiversage: Exactement. Donc, la question se résume à: pourquoi n'y a-t-il pas suffisamment d'informations stockées dans les fichiers de symboles qui indiquent également à quelle expression du code a été évaluée null.
OR Mapper
2
@MartinLiversage: Les fichiers de symboles sont accessibles de telle sorte qu'en cas d'exception, avec le message d'exception, le fichier source et le numéro de ligne puissent être affichés dans la sortie du débogueur. Donc, la question est de savoir quelle est la raison pour laquelle ne pas inclure plus d'informations sur ce qui est exactement retourné null- notez que l'OP ne prétend pas qu'il ou elle veut savoir que pour les versions de version, les versions de débogage peuvent également être suffisantes.
OR Mapper
3
Hmm, et nous ne devons pas oublier que ce n'est même pas l'IL qui s'exécute réellement, mais plutôt du code natif construit à partir de celui-ci au moment de l'exécution.
Oskar Berggren
2
@MartinLiversage: Personne dans cette question ne prétend que nous voulons que cela soit parfaitement pris en charge pour les versions de version. Quoi qu'il en soit, je ne vois pas tout à fait le problème de la corrélation de l'opcode IL qui utilise une référence d'objet (qui peut s'avérer être null) avec la ligne et la colonne du fichier source qui ont renvoyé cette référence d'objet.
OR Mapper
9

Comment voulez-vous que le message d'erreur dans le cas suivant ressemble?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Aucun nom de variable à signaler ici!

romar
la source
2
Je soupçonne que l'OP recherche l'expression dans le code source qui renvoie null, pas l'objet. J'ai ajouté un commentaire respectif à la question et j'espère qu'il ou elle éclaircira les choses. Si mes soupçons sont corrects, l'OP s'attend à quelque chose comme Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.le message d'erreur.
OR Mapper
1
@ORMapper Je suis d'accord. J'aurais juste mis ma "réponse" dans un commentaire à l'OP, si j'avais assez de points de réputation pour ajouter un commentaire!
romar
1
"une référence nulle a tenté d'appeler la méthode ToString () de la classe XYZ" serait plus utile que ce que nous obtenons maintenant.
Michael Levy
La chose la plus utile serait une trace de pile montrant, à chaque niveau d'appel, exactement quelle ligne, dans quel fichier, a généré l'erreur. Oh, attendez ... c'est ce qu'il fait maintenant!
Jim Balter
1

Eh bien, c'est aux ingénieurs de Microsoft de répondre. Mais vous pouvez évidemment utiliser un débogueur et ajouter une montre pour savoir lequel de ceux-ci a un problème.

Cependant, l'exception est NullReferenceExceptionce qui signifie que la référence n'existe pas . Vous ne pouvez pas obtenir l'objet qui n'a pas du tout été créé.

but why .NET don't tell us which object is null? Parce qu'il ne sait pas quel objet est nul. L'objet n'existe tout simplement pas!

Il en va de même lorsque je dis, C # est compilé en code .NET IL. Le code .NET IL ne connaît pas les noms ou expressions. Il ne connaît que les références et leur emplacement. Ici aussi, vous ne pouvez pas obtenir ce qui n'existe pas. L'expression ou le nom de la variable n'existe pas.

Philosophie: Vous ne pouvez pas faire d'omelette si vous n'avez pas d'oeuf en premier lieu.

Aniket Inge
la source
3
ce n'est pas une réponse non plus :)
bas
Comment obtenir la référence si elle n'existe pas? @Bas
Inge
4
"Eh bien, c'est aux ingénieurs de Microsoft de répondre.". Alors laissez-les faire la lumière sur cela au lieu de dire l'évidence
bas
@bas nous pouvons décider de ce qui est logique au minimum. Logiquement, l'objet n'existe pas. Comment allez-vous l'attraper avec une exception et ensuite imprimer le nom de l'objet? Cela n'existe tout simplement pas. Pas même sur la pile ..
Aniket Inge
donc la réponse est qu'il est pratiquement impossible d'indiquer quel objet a une référence nulle? C'est aussi une réponse. Je ne dis pas que je sais, j'aime juste la question :). +1 pour tout l'effort: p
bas
1

Pas sûr, mais c'est peut-être parce que .Net ne sait pas s'il s'agit d'une classe prédéfinie ou définie par l'utilisateur. S'il est prédéfini, il peut être nul (comme une chaîne qui occupe 2 octets), mais s'il est défini par l'utilisateur, nous devons en créer une instance pour qu'il sache que cet objet occupera autant de mémoire. Donc, il jette une erreur au moment de l'exécution.

oniel telies
la source
-2

Bonne question. La boîte de message est tout simplement inutile. Même si elle est enfouie à un kilomètre de la définition des références, une classe ou un assemblage ou un fichier ou d'autres informations seraient mieux que ce qu'ils fournissent actuellement (lire: mieux que rien).

Votre meilleure option est de l'exécuter dans le débogueur avec les informations de débogage, et votre IDE se cassera à la ligne incriminée (démontrant plutôt clairement que des informations utiles sont en fait disponibles).

Rick O'Shea
la source