Supposons que j'ai une méthode qui prend un objet comme argument. Maintenant, disons que si cette méthode reçoit un argument nul, il s’agit d’une erreur fatale et une exception doit être levée. Est-ce que cela vaut la peine pour moi de coder quelque chose comme ceci (en gardant à l'esprit qu'il s'agit d'un exemple trivial):
void someMethod(SomeClass x)
{
if (x == null){
throw new ArgumentNullException("someMethod received a null argument!");
}
x.doSomething();
}
Ou est-il prudent pour moi de simplement compter sur le lancement de NullException lorsqu'il appelle x.doSomething ()?
Deuxièmement, supposons que someMethod soit un constructeur et que x ne sera pas utilisé tant qu'une autre méthode n'aura pas été appelée. Devrais-je lancer l'exception immédiatement ou attendre que x soit nécessaire puis lancer l'exception?
Je préfère la ArgumentNullException
à la NullReferenceException
que ne pas vérifier l'argument fournirait. En général, ma préférence est de toujours vérifier la nullité avant d'essayer d'appeler une méthode sur un objet potentiellement nul.
Si la méthode est un constructeur, cela dépend de deux facteurs différents: existe-t-il également un séparateur public pour la propriété et quelle est la probabilité que l'objet soit réellement utilisé? S'il existe un outil de définition public, le fait de ne pas fournir d'instance valide via le constructeur serait raisonnable et ne devrait pas entraîner d'exception.
S'il n'y a pas de setter public et qu'il est possible d'utiliser l'objet contenant sans faire référence à l'objet injecté, vous pouvez différer la vérification/l'exception jusqu'à ce que son utilisation soit tentée. Je pense cependant que dans le cas général, l'objet injecté est essentiel au fonctionnement de l'instance et qu'une exception ArgumentNull est donc parfaitement raisonnable, car l'instance ne peut pas fonctionner sans elle.
Je suis toujours la pratique de fail fast. Si votre méthode dépend de X et que vous comprenez que X peut être passé à null, vérifiez-le et levez l'exception immédiatement au lieu de prolonger le point d'échec.
Mise à jour 2016:
Exemple du monde réel. Je recommande fortement l'utilisation de Jetbrains Annotations .
[Pure]
public static object Call([NotNull] Type declaringType,
[NotNull] string methodName,
[CanBeNull] object instance)
{
if (declaringType == null) throw new ArgumentNullException(nameof(declaringType));
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
Les instructions de garde ont été considérablement améliorées avec C # 6 fournissant l'opérateur nameof
.
Je préfère l'exception explicite, pour ces raisons:
Je suis d’accord avec l’idée d’échouer vite - mais il est sage de savoir pourquoi l’échec rapide est pratique. Considérons cet exemple:
void someMethod(SomeClass x)
{
x.Property.doSomething();
}
Si vous vous fiez à la NullReferenceException
pour vous dire que quelque chose n'allait pas, comment saurez-vous ce qui était nul? La trace de la pile ne vous donnera qu'un numéro de ligne, pas la référence nulle. Dans cet exemple, x
ou x.Property
aurait pu être nul et sans échec rapide avec une vérification agressive au préalable, vous ne saurez pas lequel.
Je préférerais aussi le paramètre check avec l’exception explicite ArgumentNullException.
En regardant les métadonnées:
//
// Summary:
// Initializes a new instance of the System.ArgumentNullException class with
// the name of the parameter that causes this exception.
//
// Parameters:
// paramName:
// The name of the parameter that caused the exception.
public ArgumentNullException(string paramName);
Vous pouvez voir que la chaîne doit être le nom du paramètre, null, et donner ainsi au développeur un indice sur ce qui ne va pas.
Il est préférable de lever l'exception ArgumentNullException le plus tôt possible. Si vous le lancez, vous pouvez fournir des informations plus utiles sur le problème qu'un NullReferenceException.
Vous devriez explicitement lever une exception ArgumentNullException si vous vous attendez à ce que l'entrée ne soit pas nulle. Vous voudrez peut-être écrire une classe appelée Guard qui fournit des méthodes d'assistance pour cela. Donc, votre code sera:
void someMethod(SomeClass x, SomeClass y)
{
Guard.NotNull(x,"x","someMethod received a null x argument!");
Guard.NotNull(y,"y","someMethod received a null y argument!");
x.doSomething();
y.doSomething();
}
La méthode NonNull effectue la vérification de la nullité et génère une exception NullArgumentException avec le message d'erreur spécifié dans l'appel.
1- Faites-le explicitement si vous ne voulez pas de valeur nulle. Sinon, lorsque quelqu'un d'autre examinera votre code, il pensera que le passage d'une valeur Null est accepté.
2- Faites-le le plus rapidement possible. De cette façon, vous ne propagerez pas le "mauvais" comportement d'avoir un Null quand ce n'est pas supposé.
Si vous programmez de manière défensive, vous devriez échouer rapidement. Vérifiez donc vos entrées et les erreurs au début de votre code. Vous devriez être gentil avec votre correspondant et lui donner le message d'erreur le plus descriptif possible.
Je vais probablement être voté pour cela, mais je pense complètement différent.
Qu'en est-il de suivre une bonne pratique appelée "Never Pass null" et supprimer la vérification des exceptions laides?
Si le paramètre est un objet, NE PAS PASSER NULL. En outre, NE RETOURNEZ PAS NULL. Vous pouvez même utiliser le modèle d'objet Null pour vous aider.
Si c'est facultatif, utilisez les valeurs par défaut (si votre langue les prend en charge) ou créez une surcharge.
Beaucoup plus propre que les exceptions laides.
Vous pouvez utiliser la syntaxe suivante pour ne pas simplement lancer une ArgumentNullException
, mais pour que cette exception nomme également le paramètre dans le texte de son erreur. Par exemple.;
void SomeMethod(SomeObject someObject)
{
Throw.IfArgNull(() => someObject);
//... do more stuff
}
La classe utilisée pour lever l'exception est;
public static class Throw
{
public static void IfArgNull<T>(Expression<Func<T>> arg)
{
if (arg == null)
{
throw new ArgumentNullException(nameof(arg), "There is no expression with which to test the object's value.");
}
// get the variable name of the argument
MemberExpression metaData = arg.Body as MemberExpression;
if (metaData == null)
{
throw new ArgumentException("Unable to retrieve the name of the object being tested.", nameof(arg));
}
// can the data type be null at all
string argName = metaData.Member.Name;
Type type = typeof(T);
if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
{
throw new ArgumentException("The expression does not specify a nullible type.", argName);
}
// get the value and check for null
if (arg.Compile()() == null)
{
throw new ArgumentNullException(argName);
}
}
}