90 votes

Vérification des paramètres nuls en C#

En C#, y a-t-il de bonnes raisons (autres qu'un meilleur message d'erreur) pour ajouter des vérifications de paramètres nuls à chaque fonction où null n'est pas une valeur valide ? De toute évidence, le code qui utilise s lèvera une exception de toute façon. Et de telles vérifications rendent le code plus lent et plus difficile à maintenir.

void f(SomeType s)
{
  if (s == null)
  {
    throw new ArgumentNullException("s cannot be null.");
  }

  // Use s
}

13 votes

Je doute sérieusement qu'une simple vérification des nullités ralentisse votre code de manière significative (ou même mesurable).

0 votes

Eh bien, cela dépend de ce que fait "Use s". Si tout ce qu'il fait est "return s.SomeField", alors la vérification supplémentaire ralentira probablement la méthode d'un ordre de grandeur.

11 votes

Probablement ? Probablement n'est pas suffisant à mes yeux. Où sont vos données de test ? Et quelle est la probabilité qu'un tel changement soit le goulot d'étranglement de votre application en premier lieu ?

175voto

Jon Skeet Points 692016

Oui, il y a de bonnes raisons :

  • Il identifie exactement ce qui est nul, ce qui peut ne pas être évident à partir d'une analyse de la situation. NullReferenceException
  • Cela fait échouer le code en cas d'entrée invalide, même si une autre condition signifie que la valeur n'est pas déréférencée.
  • Il fait en sorte que l'exception se produise avant la méthode pourrait avoir d'autres effets secondaires que vous pourriez atteindre avant la première déréférence.
  • Cela signifie que vous pouvez être sûr que si vous passez le paramètre dans quelque chose d'autre, vous ne violez pas leur contrat
  • Il documente les exigences de votre méthode (en utilisant Contrats de code est encore meilleur pour cela, bien sûr)

Maintenant, pour ce qui est de vos objections :

  • C'est plus lent : Avez-vous trouvé cela pour en fait est le goulot d'étranglement dans votre code, ou vous devinez ? Les contrôles de nullité sont très rapides, et dans la grande majorité des cas, ils ne sont pas nécessaires. no va être le goulot d'étranglement
  • Cela rend le code plus difficile à maintenir : Je pense le contraire. Je pense que c'est plus facile d'utiliser un code où il est clairement indiqué si un paramètre peut être nul ou non, et où vous êtes sûr que cette condition est respectée.

Et pour votre affirmation :

Évidemment, le code qui utilise s lèvera une exception de toute façon.

Vraiment ? Réfléchissez :

void f(SomeType s)
{
  // Use s
  Console.WriteLine("I've got a message of {0}", s);
}

Cela utilise s mais il ne lève pas d'exception. Si c'est invalide pour s est nulle, et que cela indique que quelque chose ne va pas, une exception est le comportement le plus approprié ici.

Maintenant que vous placez ces contrôles de validation des arguments est une autre question. Vous pouvez décider de faire confiance à tout le code de votre propre classe, et donc de ne pas vous préoccuper des méthodes privées. Vous pouvez décider de faire confiance au reste de votre assemblage, et donc de ne pas vous préoccuper des méthodes internes. Vous devriez presque certainement valider les arguments des méthodes publiques.

Remarque : la surcharge du constructeur à paramètre unique de l'option ArgumentNullException devrait juste être le nom du paramètre, donc votre test devrait être :

if (s == null)
{
  throw new ArgumentNullException("s");
}

Alternativement, vous pouvez créer une méthode d'extension, permettant de terser quelque peu :

s.ThrowIfNull("s");

Dans ma version de la méthode d'extension (générique), je fais en sorte qu'elle renvoie la valeur originale si elle n'est pas nulle, ce qui vous permet d'écrire des choses comme :

this.name = name.ThrowIfNull("name");

Vous pouvez également avoir une surcharge qui ne prend pas le nom du paramètre, si cela ne vous dérange pas trop.

8 votes

+1 pour la méthode d'extension. J'ai fait exactement la même chose, en incluant d'autres validations telles que ThrowIfEmpty en ICollection

5 votes

Pourquoi je pense que c'est plus difficile à maintenir : comme tout mécanisme manuel qui nécessite l'intervention du programmeur à chaque fois, cela est sujet à des erreurs et des omissions et encombre le code. Une sécurité bancale est pire que pas de sécurité du tout.

8 votes

@kaalus : Appliquez-vous la même attitude aux tests ? "Mes tests n'attraperont pas tous les bugs possibles, donc je n'en écrirai pas" ? Lorsque les mécanismes de sécurité faire Ils faciliteront la recherche du problème et réduiront l'impact ailleurs (en détectant le problème plus tôt). Si cela se produit 9 fois sur 10, c'est toujours mieux que si cela se produit 0 fois sur 10...

53voto

Eric Lippert Points 300275

Je suis d'accord avec Jon, mais j'ajouterais une chose à cela.

Mon attitude sur le moment où il faut ajouter des contrôles explicites de nullité est basée sur ces prémisses :

  • Il devrait y avoir un moyen pour vos tests unitaires d'exercer chaque déclaration dans un programme.
  • throw les déclarations sont déclarations .
  • La conséquence d'un if est un déclaration .
  • Par conséquent, il devrait y avoir un moyen d'exercer la throw en if (x == null) throw whatever;

S'il y a aucun moyen possible pour que cette déclaration soit exécutée, alors elle ne peut pas être testée et doit être remplacée par Debug.Assert(x != null); .

S'il existe un moyen possible d'exécuter cette instruction, écrivez-la, puis écrivez un test unitaire qui l'exerce.

Il est notamment Il est important que les méthodes publiques des types publics vérifient leurs arguments de cette manière ; vous n'avez aucune idée des folies que vos utilisateurs vont faire. Donnez-leur l'exception "Hé, crétin, tu te trompes" dès que possible.

Les méthodes privées de types privés, en revanche, sont beaucoup plus susceptibles de se trouver dans la situation où vous contrôlez les arguments et pouvez avoir une forte garantie que l'argument n'est jamais nul ; utilisez une assertion pour documenter cet invariant.

9voto

SLaks Points 391154

En l'absence d'un if vérifier, il peut être très difficile de comprendre ce que était null si vous ne possédez pas le code.

Si vous obtenez un NullReferenceException à l'intérieur d'une bibliothèque sans code source, vous aurez probablement beaucoup de mal à trouver ce que vous avez fait de mal.

Ces if ne rendront pas votre code sensiblement plus lent.


Notez que le paramètre de la méthode ArgumentNullException Constructeur est un nom de paramètre, pas un message.
Votre code devrait être

if (s == null) throw new ArgumentNullException("s");

J'ai écrit un extrait de code pour rendre cela plus facile :

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Check for null arguments</Title>
            <Shortcut>tna</Shortcut>
            <Description>Code snippet for throw new ArgumentNullException</Description>
            <Author>SLaks</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>Parameter</ID>
                    <ToolTip>Paremeter to check for null</ToolTip>
                    <Default>value</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$");
        $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

5voto

AD.Net Points 8444

Vous voudrez peut-être jeter un coup d'œil à Contrats de code si vous avez besoin d'un moyen plus agréable de vous assurer que vous ne recevez pas d'objets nuls en tant que paramètre.

2voto

Justin Niessner Points 144953

Le principal avantage est que vous êtes explicite quant aux exigences de votre méthode dès le départ. Ainsi, les autres développeurs travaillant sur le code savent clairement que l'appelant qui envoie une valeur nulle à votre méthode commet une erreur.

Le contrôle arrêtera également l'exécution de la méthode avant que tout autre code ne s'exécute. Cela signifie que vous n'aurez pas à vous inquiéter des modifications apportées par la méthode qui restent inachevées.

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