Question

Je viens de commencer à parcourir « Débogage des applications MS .Net 2.0 » de John Robbins, et je suis devenu confus par son évangélisation pour Debug.Assert(...).

Il souligne que les assertions bien implémentées stockent, dans une certaine mesure, l'état d'une condition d'erreur, par exemple :

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

Maintenant, personnellement, cela me semble fou qu'il aime tant reformuler son test sans un véritable commentaire sensé sur la « logique métier », peut-être que « i <= 3 ne doit jamais arriver à cause du processus de widgitification flobittyjam ».

Donc, je pense que je reçois les assertions comme une sorte de chose de bas niveau "Protégeons mes hypothèses"...en supposant que l'on pense qu'il s'agit d'un test qu'il suffit de faire en débogage - c'est-à-direvous vous protégez contre vos collègues et futurs programmeurs et espérez qu'ils testent réellement les choses.

Mais ce que je ne comprends pas, c'est qu'il continue en disant que vous devez utiliser des assertions en plus de la gestion normale des erreurs ;maintenant, ce que j'envisage ressemble à ceci :

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

Qu'ai-je gagné grâce à la répétition Debug.Assert du test de condition d'erreur ?Je pense que je comprendrais si nous parlions de double-vérification uniquement de débogage d'un calcul très important...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

... mais je ne l'obtiens pas pour les tests de paramètres qui valent sûrement la peine d'être vérifiés (dans les versions DEBUG et Release)...ou non.Qu'est-ce que je rate?

Était-ce utile?

La solution

Les assertions ne sont pas destinées à la vérification des paramètres.La vérification des paramètres doit toujours être effectuée (et précisément en fonction des conditions préalables spécifiées dans votre documentation et/ou spécification), et le ArgumentOutOfRangeException jeté si nécessaire.

Les assertions servent à tester des situations « impossibles », c'est-à-dire des choses que vous (dans la logique de votre programme) supposer sont vrai.Les assertions sont là pour vous dire si ces hypothèses sont brisées pour une raison quelconque.

J'espère que cela t'aides!

Autres conseils

Il y a un aspect de communication entre les assertions et le lancement d'exceptions.

Disons que nous avons une classe User avec une propriété Name et une méthode ToString.

Si ToString est implémenté comme ceci :

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

Il indique que Name ne doit jamais être nul et qu'il y a un bug dans la classe User si c'est le cas.

Si ToString est implémenté comme ceci :

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Il indique que l'appelant utilise ToString de manière incorrecte si Name est nul et doit le vérifier avant d'appeler.

La mise en œuvre avec les deux

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

dit que si Name est nul, il y a un bug dans la classe User, mais nous voulons quand même le gérer.(L'utilisateur n'a pas besoin de vérifier le nom avant d'appeler.) Je pense que c'est le genre de sécurité que Robbins recommandait.

J'y ai réfléchi longuement et sérieusement lorsqu'il s'agit de fournir des conseils sur le débogage et le développement.faire valoir en ce qui concerne les problèmes liés aux tests.

Vous devriez être en mesure de tester votre classe avec une entrée erronée, un mauvais état, un ordre d'opérations invalide et toute autre condition d'erreur imaginable et une assertion devrait jamais voyage.Chaque assert vérifie quelque chose qui devrait toujours être vrai quelles que soient les entrées ou les calculs effectués.

Bonnes règles empiriques auxquelles je suis arrivé :

  1. Les assertions ne remplacent pas un code robuste qui fonctionne correctement indépendamment de la configuration.Ils sont complémentaires.

  2. Les assertions ne doivent jamais être déclenchées pendant une exécution de test unitaire, même en cas d'introduction de valeurs non valides ou de conditions d'erreur de test.Le code doit gérer ces conditions sans qu'une assertion ne se produise.

  3. Si une assertion se déclenche (soit dans un test unitaire, soit pendant un test), la classe est buggée.

Pour toutes les autres erreurs - généralement dues à l'environnement (connexion réseau perdue) ou à une mauvaise utilisation (l'appelant a transmis une valeur nulle) - il est beaucoup plus agréable et plus compréhensible d'utiliser des vérifications et des exceptions strictes.Si une exception se produit, l'appelant sait que c'est probablement de sa faute.Si une assertion se produit, l'appelant sait qu'il s'agit probablement d'un bug dans le code où se trouve l'assertion.

Concernant la duplication :Je suis d'accord.Je ne vois pas pourquoi vous répliqueriez la validation avec un Debug.Assert ET une vérification d'exception.Non seulement cela ajoute du bruit au code et brouille les pistes concernant la question de savoir qui est en faute, mais c'est aussi une forme de répétition.

J'utilise des vérifications explicites qui lèvent des exceptions publique et protégé méthodes et assertions sur les méthodes privées.

Habituellement, les vérifications explicites empêchent de toute façon les méthodes privées de voir des valeurs incorrectes.Donc en réalité, l'assertion vérifie une condition qui devrait être impossible.Si une assertion se déclenche, elle m'indique qu'il y a un défaut dans la logique de validation contenue dans l'une des routines publiques de la classe.

Une exception peut être détectée et avalée, rendant l'erreur invisible aux tests.Cela ne peut pas arriver avec Debug.Assert.

Personne ne devrait jamais avoir un gestionnaire catch qui intercepte toutes les exceptions, mais les gens le font quand même, et parfois c'est inévitable.Si votre code est appelé depuis COM, la couche d'interopérabilité intercepte toutes les exceptions et les transforme en codes d'erreur COM, ce qui signifie que vous ne verrez pas vos exceptions non gérées.Les assertions n'en souffrent pas.

De plus, lorsque l'exception n'est pas gérée, une pratique encore meilleure consiste à effectuer un mini-dump.Un domaine dans lequel VB est plus puissant que C# est que vous pouvez utiliser un filtre d'exception pour créer un mini-dump lorsque l'exception est en cours et laisser le reste de la gestion des exceptions inchangé. Article de blog de Gregg Miskelly sur l'injection de filtre d'exception fournit un moyen utile de le faire à partir de c#.

Une autre note sur les actifs...ils interagissent mal avec Unit testant les conditions d'erreur dans votre code.Cela vaut la peine d'avoir un wrapper pour désactiver l'assertion pour vos tests unitaires.

OMI, c'est uniquement une perte de temps de développement.Une exception correctement mise en œuvre vous donne une image claire de ce qui s’est passé.J'ai vu trop applications affichant un obscur « Échec de l'assertion :j'ai < 10" d'erreurs.Je considère l'affirmation comme une solution temporaire.À mon avis, aucune affirmation ne devrait figurer dans la version finale d'un programme.Dans ma pratique, j'ai utilisé des assertions pour des contrôles rapides et sales.La version finale du code doit prendre en compte la situation erronée et se comporter en conséquence.Si quelque chose de grave arrive, vous avez 2 choix :manipulez-le ou laissez-le.La fonction doit lever une exception avec une description significative si de mauvais paramètres sont transmis.Je ne vois aucun intérêt à dupliquer la logique de validation.

Exemple d’une bonne utilisation d’Assert :

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

Personnellement, je pense qu'Assert devrait seulement être utilisé quand vous savez qu'il y a quelque chose à l'extérieur souhaitable limites, mais vous pouvez être sûr qu'il est raisonnablement sûr de continuer.Dans toutes les autres circonstances (n'hésitez pas à signaler les circonstances auxquelles je n'ai pas pensé), utilisez des exceptions pour échouer durement et rapidement.

Le compromis clé pour moi est de savoir si vous souhaitez arrêter un système live/production avec une exception pour éviter la corruption et faciliter le dépannage, ou si vous avez rencontré une situation qui ne devrait jamais passer inaperçue dans les versions de test/débogage mais qui pourrait être autorisé à continuer la production (en enregistrant un avertissement bien sûr).

cf. http://c2.com/cgi/wiki?FailFastcopié et modifié à partir de la question Java : Exception contre assertion

Voici par 2 cents.

Je pense que la meilleure façon est d'utiliser à la fois des assertions et des exceptions.Les principales différences entre les deux méthodes, à mon humble avis, si les instructions Assert peuvent être facilement supprimées du texte de l'application (définitions, attributs conditionnels...), tandis que les exceptions levées dépendent (généralement) d'un code conditionnel qui est plus difficile à supprimer ( section multine avec conditions du préprocesseur).

Chaque exception d'application doit être traitée correctement, tandis que les assertions ne doivent être satisfaites que pendant le développement et les tests de l'algorithme.

Si vous transmettez une référence d’objet nulle en tant que paramètre de routine et que vous utilisez cette valeur, vous obtenez une exception de pointeur nul.En effet:pourquoi devriez-vous écrire une affirmation ?C'est une perte de temps dans ce cas.Mais qu’en est-il des membres des classes privées utilisés dans les routines de classe ?Lorsque ces valeurs sont définies quelque part, il est préférable de vérifier avec une assertion si une valeur nulle est définie.C'est uniquement parce que lorsque vous utilisez le membre, vous obtenez une exception de pointeur nul mais vous ne savez pas comment la valeur a été définie.Cela provoque un redémarrage du programme qui s'interrompt sur tous les points d'entrée utilisés pour définir le membre privé.

Les exceptions sont plus utiles, mais elles peuvent être (à mon humble avis) très lourdes à gérer et il est possible d'utiliser trop d'exceptions.Et ils nécessitent une vérification supplémentaire, peut-être indésirable pour optimiser le code.Personnellement, j'utilise des exceptions uniquement lorsque le code nécessite un contrôle catch approfondi (les instructions catch sont très faibles dans la pile d'appels) ou lorsque les paramètres de fonction ne sont pas codés en dur dans le code.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top