Disons que j'ai une méthode:
public void DoSomething(ISomeInterface someObject)
{
if(someObject == null) throw new ArgumentNullException("someObject");
someObject.DoThisOrThat();
}
J'ai été formé pour croire que lancer le ArgumentNullException
est "correct" mais une erreur "Référence d'objet non définie sur une instance d'un objet" signifie que j'ai un bug.
Pourquoi?
Je sais que si je mettais en cache la référence someObject
et que je l' utilisais plus tard, il est préférable de vérifier la nullité lorsqu'elle est transmise et d'échouer tôt. Cependant, si je fais un déréférencement sur la ligne suivante, pourquoi sommes-nous censés faire le contrôle? Cela va lever une exception dans un sens ou dans l'autre.
Modifier :
Cela m'est venu à l'esprit ... la peur du null déréférencé vient-elle d'un langage comme C ++ qui ne vérifie pas pour vous (c'est-à-dire qu'il essaie simplement d'exécuter une méthode à l'emplacement de mémoire zéro + décalage de méthode)?
Réponses:
Je suppose que si vous déréférencez immédiatement la variable, vous pouvez débattre dans les deux sens, mais je préférerais toujours l'argumentNullException.
Il est beaucoup plus explicite de ce qui se passe. L'exception contient le nom de la variable qui était null, contrairement à une exception NullReferenceException. En particulier au niveau de l'API, une ArgumentNullException indique clairement qu'un appelant n'a pas respecté le contrat d'une méthode, et l'échec n'était pas nécessairement une erreur aléatoire ou un problème plus profond (bien que cela puisse toujours être le cas). Vous devriez échouer tôt.
De plus, qu'est-ce que les mainteneurs ultérieurs sont plus susceptibles de faire? Ajouter l'argument ArgumentNullException s'ils ajoutent du code avant la première déréférence, ou simplement ajouter le code et autoriser ultérieurement une exception NullReferenceException?
la source
Supposons que j'appelle la méthode
M(x)
que vous avez écrite. Je passe nul. J'obtiens une ArgumentNullException avec le nom défini sur "x". Cette exception signifie sans équivoque que j'ai un bug; Je n'aurais pas dû passer null pour x.Supposons que j'appelle une méthode M que vous avez écrite. Je passe nul. Je reçois une exception null deref. Comment diable suis-je censé savoir si j'ai un bogue ou si vous avez un bogue ou si une bibliothèque tierce que vous avez appelée en mon nom a un bogue? Je ne sais pas si je dois réparer mon code ou vous appeler pour vous dire de réparer le vôtre.
Les deux exceptions indiquent des bogues; ni ne devrait jamais être jeté dans le code de production. La question est de savoir quelle exception communique qui a le bogue le mieux à la personne qui effectue le débogage? L'exception nulle deref ne vous dit presque rien; L'argument null exception vous en dit long. Soyez gentil avec vos utilisateurs; lever des exceptions qui leur permettent d'apprendre de leurs erreurs.
la source
"neither should ever be thrown in production code"
Quels autres types de code / situations seraient-ils utilisés? Doivent-ils être compilés commeDebug.Assert
? Ou voulez-vous dire ne les jetez pas hors d'une API?throw new ArgumentNullException
dans votre code de production, et il ne devrait jamais s'exécuter en dehors d'un cas de test . L'exception ne doit jamais être levée car l'appelant ne doit jamais avoir ce bogue.Le fait est que votre code doit faire quelque chose de significatif.
A
NullReferenceException
n'est pas particulièrement significatif, car il pourrait signifier tout. Sans regarder où l'exception a été levée (et le code pourrait ne pas être disponible pour moi), je ne peux pas comprendre ce qui s'est mal passé.Un
ArgumentNullException
est significatif, car il me dit: l'opération a échoué parce que l'argument l'someObject
étaitnull
. Je n'ai pas besoin de regarder votre code pour le comprendre.Élever également cette exception signifie que vous gérez explicitement
null
, même si votre seule façon de le faire est de dire que vous ne pouvez pas le gérer, alors que laisser le code planter pourrait potentiellement signifier que vous pourriez en fait gérer null , mais j'ai oublié de mettre en œuvre le cas.Le point d'exceptions est de communiquer des informations en cas d'échec, qui peuvent être gérées au moment de l'exécution pour récupérer de l'échec, ou au moment du débogage pour mieux comprendre ce qui s'est passé.
la source
Je crois que son seul avantage est que vous pouvez fournir de meilleurs diagnostics à l'exception. C'est que vous savez, que l'appelant de la fonction passe null, alors que si vous laissez la déréférence le jeter, vous ne seriez pas sûr si l'appelant passe des données incorrectes ou s'il y a un bogue dans la fonction elle-même.
Je le ferais si quelqu'un d'autre appelait la fonction pour la rendre plus facile à utiliser (déboguer en cas de problème) et ne pas le faire dans mon code interne, car le runtime le vérifierait de toute façon.
la source
Vous avez un bogue si le code ne fonctionne pas comme prévu. Un
NullReferenceException
est jeté par le système et ce fait fait vraiment plus un bug qu'une fonctionnalité. Non seulement parce que vous n'avez pas défini l'exception spécifique à gérer. De plus, un développeur qui lit votre code peut supposer que vous n'avez pas tenu compte de la situation.Pour des raisons similaires, l'
ApplicationException
appartenance à votre application vs un généralException
qui appartient au système d'exploitation. Le type d'exception levée indique l'origine de l'exception.Si vous avez comptabilisé null, il est préférable de le gérer avec élégance en lançant une exception spécifique ou s'il s'agit d'une entrée acceptable, alors ne lancez pas d'exception car elles sont coûteuses. Gérez-le en effectuant des opérations avec de bonnes valeurs par défaut ou aucune opération (par exemple, aucune mise à jour requise).
Enfin, ne négligez pas la capacité de l'appelant à gérer la situation. En leur donnant une exception plus spécifique,
ArgumentException
ils peuvent construire leur try / catch de manière à gérer cette erreur avant la plus généraleNullReferenceException
qui peut être causée par un certain nombre d'autres raisons qui se produisent ailleurs, comme un échec d'initialisation d'une variable constructeur.la source
La vérification rend plus explicite ce qui se passe, mais le vrai problème vient avec du code comme cet exemple (artificiel):
Ici, une chaîne d'appels est impliquée et sans vérification nulle, vous ne voyez que l'erreur dans someOtherObject et pas là où le problème s'est révélé pour la première fois - ce qui signifie instantanément une perte de temps de débogage ou d'interprétation des piles d'appels.
En général, il vaut mieux être cohérent avec ce genre de chose et toujours ajouter la vérification null - ce qui facilite plus de repérer si une est manquante plutôt que de choisir et de choisir au cas par cas (en supposant que vous savez que null est toujours invalide).
la source
Une autre raison à considérer est que si un traitement se produit avant que l'objet nul ne soit utilisé?
Supposons que vous ayez un fichier de données d'ID d'entreprise associés (Cid) et d'ID de personne (Pid). Il est enregistré sous forme de flux de paires de nombres:
Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid
Et cette fonction VB écrit les enregistrements dans le fichier:
Si
Person
est une référence nulle, la ligneFile.Write(Person.ID)
lèvera une exception. Le logiciel peut intercepter l'exception et récupérer, mais cela laisse un problème. Un ID d'entreprise a été écrit sans ID de personne associé. Si tel est le cas, lors de votre prochain appel à cette fonction, vous allez mettre un ID d'entreprise là où l'ID de personne précédente était attendu. En plus que cet enregistrement soit incorrect, chaque enregistrement ultérieur aura un ID de personne suivi d'un ID de société, ce qui est le mauvais sens.Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid
En validant les paramètres au début de la fonction, vous empêchez tout traitement de se produire lorsque la méthode est garantie d'échouer.
la source