Je suis ingénieur logiciel professionnel depuis environ un an maintenant, après avoir obtenu un diplôme CS. Je connais les assertions depuis un certain temps en C ++ et C, mais je ne savais pas du tout qu'elles existaient en C # et .NET jusqu'à récemment.
Notre code de production ne contient aucune assertion que ce soit et ma question est la suivante ...
Dois-je commencer à utiliser Asserts dans notre code de production? Et si oui, quand son utilisation est-elle la plus appropriée? Serait-il plus logique de faire
Debug.Assert(val != null);
ou
if ( val == null )
throw new exception();
language-agnostic
exception
testing
assertions
defensive-programming
Nicholas Mancuso
la source
la source
Réponses:
Dans le débogage des applications Microsoft .NET 2.0, John Robbins a une grande section sur les assertions. Ses principaux points sont:
PS: Si vous avez aimé Code Complete, je vous recommande de le suivre avec ce livre. Je l'ai acheté pour en savoir plus sur l'utilisation de WinDBG et des fichiers de vidage, mais la première moitié est remplie de conseils pour éviter les bogues en premier lieu.
la source
Debug.Assert
contreTrace.Assert
. Ce dernier est exécuté dans une version Release ainsi qu'une version Debug.Mettez
Debug.Assert()
partout dans le code où vous voulez avoir des contrôles d'intégrité pour garantir les invariants. Lorsque vous compilez une version Release (c.-à-d. AucuneDEBUG
constante de compilation), les appels àDebug.Assert()
seront supprimés afin de ne pas affecter les performances.Vous devez toujours lever des exceptions avant d'appeler
Debug.Assert()
. L'assertion s'assure simplement que tout est comme prévu pendant que vous êtes encore en développement.la source
À partir du code terminé
la source
FWIW ... Je trouve que mes méthodes publiques ont tendance à utiliser le
if () { throw; }
modèle pour s'assurer que la méthode est appelée correctement. Mes méthodes privées ont tendance à utiliserDebug.Assert()
.L'idée est qu'avec mes méthodes privées, je suis celle sous contrôle, donc si je commence à appeler l'une de mes propres méthodes privées avec des paramètres incorrects, alors j'ai brisé ma propre hypothèse quelque part - je n'aurais jamais dû obtenir dans cet état. En production, ces affirmations privées devraient idéalement être un travail inutile car je suis censé garder mon état interne valide et cohérent. Contrairement aux paramètres donnés aux méthodes publiques, qui peuvent être appelés par n'importe qui au moment de l'exécution: j'ai encore besoin d'imposer des contraintes de paramètres en lançant des exceptions.
De plus, mes méthodes privées peuvent toujours lever des exceptions si quelque chose ne fonctionne pas au moment de l'exécution (erreur réseau, erreur d'accès aux données, mauvaises données récupérées à partir d'un service tiers, etc.). Mes affirmations sont juste là pour m'assurer que je n'ai pas brisé mes propres hypothèses internes sur l'état de l'objet.
la source
Utilisez les assertions pour vérifier les hypothèses du développeur et les exceptions pour vérifier les hypothèses environnementales.
la source
Si j'étais vous je ferais:
Ou pour éviter un contrôle d'état répété
la source
Si vous voulez des Asserts dans votre code de production (c'est-à-dire des versions Release), vous pouvez utiliser Trace.Assert au lieu de Debug.Assert.
Bien sûr, cela ajoute des frais généraux à votre exécutable de production.
De plus, si votre application s'exécute en mode interface utilisateur, la boîte de dialogue Assertion s'affiche par défaut, ce qui peut être un peu déconcertant pour vos utilisateurs.
Vous pouvez remplacer ce comportement en supprimant le DefaultTraceListener: consultez la documentation de Trace.Listeners dans MSDN.
En résumé,
Utilisez Debug.Assert généreusement pour aider à détecter les bogues dans les versions Debug.
Si vous utilisez Trace.Assert en mode interface utilisateur, vous souhaiterez probablement supprimer DefaultTraceListener pour éviter de déconcerter les utilisateurs.
Si la condition que vous testez est quelque chose que votre application ne peut pas gérer, vous feriez probablement mieux de lever une exception pour vous assurer que l'exécution ne se poursuit pas. Sachez qu'un utilisateur peut choisir d'ignorer une assertion.
la source
Les assertions sont utilisées pour intercepter l'erreur de votre programmeur, pas l'erreur de l'utilisateur. Ils ne doivent être utilisés que lorsqu'il n'y a aucune chance qu'un utilisateur puisse provoquer l'assertion à se déclencher. Si vous écrivez une API, par exemple, les assertions ne doivent pas être utilisées pour vérifier qu'un argument n'est pas nul dans une méthode qu'un utilisateur d'API pourrait appeler. Mais il pourrait être utilisé dans une méthode privée non exposée dans le cadre de votre API pour affirmer que VOTRE code ne passe jamais un argument nul lorsqu'il n'est pas censé le faire.
Je préfère généralement les exceptions aux assertions lorsque je ne suis pas sûr.
la source
En bref
Asserts
sont utilisés pour les gardes et pour vérifier les contraintes de conception par contrat, à savoir:Asserts
devrait être pour les versions de débogage et non-production uniquement. Les assertions sont généralement ignorées par le compilateur dans les versions Release.Asserts
peut vérifier les bogues / conditions inattendues qui SONT sous le contrôle de votre systèmeAsserts
NE SONT PAS un mécanisme de validation de première ligne des entrées utilisateur ou des règles métierAsserts
ne doit pas être utilisé pour détecter des conditions environnementales inattendues (qui échappent au contrôle du code), par exemple, manque de mémoire, défaillance du réseau, défaillance de la base de données, etc. Bien que rares, ces conditions sont à prévoir (et le code de votre application ne peut pas résoudre des problèmes tels que panne matérielle ou épuisement des ressources). En règle générale, des exceptions seront levées - votre application peut alors soit prendre des mesures correctives (par exemple réessayer une opération de base de données ou de réseau, tenter de libérer de la mémoire cache), soit abandonner gracieusement si l'exception ne peut pas être gérée.Asserts
- votre code fonctionne en territoire inattendu. Les traces de pile et les vidages sur incident peuvent être utilisés pour déterminer ce qui n'a pas fonctionné.Les affirmations ont un énorme avantage:
Debug
versions.... Plus de détails
Debug.Assert
exprime une condition qui a été supposée sur l'état par le reste du bloc de code sous le contrôle du programme. Cela peut inclure l'état des paramètres fournis, l'état des membres d'une instance de classe ou le retour d'un appel de méthode dans sa plage contractée / conçue. En règle générale, les assertions doivent planter le thread / processus / programme avec toutes les informations nécessaires (trace de pile, vidage sur incident, etc.), car elles indiquent la présence d'un bogue ou d'une condition non prise en compte qui n'a pas été conçue pour (c.-à-d. N'essayez pas d'attraper ou de gérer les échecs d'assertion), à une exception possible quand une assertion elle-même pourrait causer plus de dommages que le bogue (par exemple, les contrôleurs de la circulation aérienne ne voudraient pas d'un YSOD lorsqu'un aéronef passe en sous-marin, bien qu'il soit inutile de savoir si une version de débogage doit être déployée pour production ...)Quand devez-vous utiliser
Asserts?
- À tout moment dans un système, une API de bibliothèque ou un service où les entrées d'une fonction ou de l'état d'une classe sont supposées valides (par exemple, lorsque la validation a déjà été effectuée sur les entrées utilisateur dans le niveau de présentation d'un système , les classes métier et données supposent généralement que les vérifications nulles, les vérifications de plage, les vérifications de longueur de chaîne, etc. en entrée ont déjà été effectuées). - LesAssert
vérifications courantes incluent les cas où une hypothèse invalide entraînerait une déréférence nulle de l'objet, un diviseur nul, un dépassement arithmétique numérique ou de la date, et hors bande général / non conçu pour le comportement (par exemple, si un entier 32 bits a été utilisé pour modéliser l'âge d'un être humain). , il serait prudentAssert
que l'âge soit en réalité compris entre 0 et 125 environ - les valeurs de -100 et 10 ^ 10 n'ont pas été conçues pour).Contrats de code .Net
Dans la pile .Net, les contrats de code peuvent être utilisés en complément ou en remplacement de l' utilisation
Debug.Assert
. Les contrats de code peuvent formaliser davantage la vérification de l'état et peuvent aider à détecter les violations d'hypothèses au moment de la compilation (ou peu de temps après, s'ils sont exécutés en tant que vérification des antécédents dans un IDE).Les contrôles de conception par contrat (DBC) disponibles comprennent:
Contract.Requires
- Conditions préalables contractuellesContract.Ensures
- Postconditions contractuellesInvariant
- Exprime une hypothèse sur l'état d'un objet à tous les points de sa durée de vie.Contract.Assumes
- pacifie le vérificateur statique lorsqu'un appel à des méthodes décorées non contractuelles est effectué.la source
Surtout jamais dans mon livre. Dans la grande majorité des cas, si vous voulez vérifier si tout est sain, jetez-le si ce n'est pas le cas.
Ce que je n'aime pas, c'est le fait que cela rend une version de débogage fonctionnellement différente d'une version. Si une assertion de débogage échoue mais que la fonctionnalité fonctionne dans la version, comment cela a-t-il un sens? C'est encore mieux lorsque l'asserteur a quitté l'entreprise depuis longtemps et que personne ne connaît cette partie du code. Ensuite, vous devez tuer une partie de votre temps à explorer le problème pour voir s'il s'agit vraiment d'un problème ou non. Si c'est un problème, pourquoi la personne ne lance-t-elle pas en premier lieu?
Pour moi, cela suggère en utilisant Debug.Assert que vous reportez le problème à quelqu'un d'autre, traitez le problème vous-même. Si quelque chose est censé être le cas et ce n'est pas le cas, lancez-le.
Je suppose qu'il existe peut-être des scénarios de performances critiques où vous souhaitez optimiser vos assertions et ils sont utiles là-bas, mais je n'ai pas encore rencontré un tel scénario.
la source
System.Diagnostics.Trace.Assert()
s'exécute dans une version Release ainsi qu'une version Debug.Selon la norme IDesign , vous devez
Comme avertissement, je dois mentionner que je n'ai pas trouvé pratique de mettre en œuvre cet IRL. Mais c'est leur standard.
la source
Utilisez les assertions uniquement dans les cas où vous souhaitez supprimer la vérification des versions. N'oubliez pas que vos assertions ne se déclencheront pas si vous ne compilez pas en mode débogage.
Compte tenu de votre exemple de vérification de la nullité, s'il s'agit d'une API interne uniquement, je pourrais utiliser une assertion. Si c'est dans une API publique, j'utiliserais certainement la vérification et la levée explicites.
la source
System.Diagnostics.Trace.Assert()
pour exécuter une assertion dans une version de version (production).null
quand: "Une méthode visible de l'extérieur déréférence l'un de ses arguments de référence sans vérifier si cet argument est nul ." Dans une telle situation, la méthode ou la propriété doit lancerArgumentNullException
.Toutes les assertions doivent être du code pouvant être optimisé pour:
Parce que vérifier quelque chose que vous avez déjà supposé est vrai. Par exemple:
Dans ce qui précède, il existe trois approches différentes des paramètres nuls. Le premier l'accepte comme autorisé (il ne fait rien). Le second lève une exception pour le code appelant à gérer (ou non, ce qui entraîne un message d'erreur). Le troisième suppose que cela ne peut pas arriver et affirme qu'il en est ainsi.
Dans le premier cas, il n'y a pas de problème.
Dans le second cas, il y a un problème avec le code appelant - il n'aurait pas dû appeler
GetFirstAndConsume
avec null, donc il récupère une exception.Dans le troisième cas, il y a un problème avec ce code, car il aurait déjà dû être vérifié
en != null
avant qu'il ne soit appelé, de sorte qu'il n'est pas vrai est un bug. Ou en d'autres termes, ce devrait être du code qui pourrait théoriquement être optimiséDebug.Assert(true)
, sicneen != null
devrait toujours l'êtretrue
!la source
en == null
en production? Êtes-vous en train de dire queen == null
cela ne peut jamais arriver en production (puisque le programme a été complètement débogué)? Si c'est le cas, alorsDebug.Assert(en != null)
à tout le moins sert d'alternative à un commentaire. Bien sûr, si des modifications futures sont apportées, il continue également d'avoir une valeur pour détecter une régression possible.Debug.Assert()
sont supprimés dans une version Release. Donc, si vous êtes mal, dans le troisième cas , vous ne saurez pas dans la production ( en supposant l'utilisation d'une accumulation de presse dans la production). Cependant, le comportement des premier et deuxième cas est identique dans les versions Debug et Release.J'ai pensé que j'ajouterais quatre autres cas, où Debug.Assert peut être le bon choix.
1) Quelque chose que je n'ai pas vu mentionné ici est la couverture conceptuelle supplémentaire que les Asserts peuvent fournir pendant les tests automatisés . Comme exemple simple:
Lorsqu'un appelant de niveau supérieur est modifié par un auteur qui pense avoir étendu la portée du code pour gérer des scénarios supplémentaires, idéalement (!) Ils écriront des tests unitaires pour couvrir cette nouvelle condition. Il se peut alors que le code entièrement intégré semble fonctionner correctement.
Cependant, en réalité, une faille subtile a été introduite, mais non détectée dans les résultats des tests. Le non est devenu callee déterministe dans ce cas, et seulement arrive à donner le résultat escompté. Ou peut-être qu'il a produit une erreur d'arrondi qui n'a pas été remarquée. Ou a provoqué une erreur qui a été compensée également ailleurs. Ou accordé non seulement l'accès demandé mais des privilèges supplémentaires qui ne devraient pas être accordés. Etc.
À ce stade, les instructions Debug.Assert () contenues dans l'appelé couplées au nouveau cas (ou cas de bord) entraîné par des tests unitaires peuvent fournir une notification inestimable pendant le test que les hypothèses de l'auteur d'origine ont été invalidées, et le code ne doit pas être publié sans examen supplémentaire. Les assertions avec tests unitaires sont les partenaires parfaits.
2) De plus, certains tests sont simples à écrire, mais coûteux et inutiles compte tenu des hypothèses initiales . Par exemple:
Si un objet n'est accessible qu'à partir d'un certain point d'entrée sécurisé, une requête supplémentaire doit-elle être effectuée dans une base de données de droits réseau à partir de chaque méthode d'objet pour garantir que l'appelant dispose des autorisations? Sûrement pas. Peut-être que la solution idéale comprend la mise en cache ou une autre extension des fonctionnalités, mais la conception ne l'exige pas. Un Debug.Assert () affichera immédiatement lorsque l'objet a été attaché à un point d'entrée non sécurisé.
3) Ensuite, dans certains cas, votre produit peut n'avoir aucune interaction de diagnostic utile pour tout ou partie de ses opérations lorsqu'il est déployé en mode de publication . Par exemple:
Supposons qu'il s'agisse d'un périphérique embarqué en temps réel. Lancer des exceptions et redémarrer lorsqu'il rencontre un paquet mal formé est contre-productif. Au lieu de cela, l'appareil peut bénéficier d'un fonctionnement au meilleur effort, même au point de rendre du bruit dans sa sortie. Il peut également ne pas avoir d'interface humaine, de périphérique de journalisation ou même être physiquement accessible par l'homme lorsqu'il est déployé en mode de publication, et la sensibilisation aux erreurs est mieux fournie en évaluant la même sortie. Dans ce cas, les assertions libérales et les tests de pré-publication approfondis sont plus précieux que les exceptions.
4) Enfin, certains tests sont inutiles uniquement parce que l'appelé est perçu comme extrêmement fiable . Dans la plupart des cas, plus le code est réutilisable, plus l'effort a été mis pour le rendre fiable. Par conséquent, il est courant d'exceptionner les paramètres inattendus des appelants, mais d'assert pour les résultats inattendus des appels. Par exemple:
Si une
String.Find
opération principale indique qu'elle renverra un-1
lorsque les critères de recherche ne sont pas trouvés, vous pourrez peut-être effectuer en toute sécurité une opération plutôt que trois. Cependant, s'il est effectivement retourné-2
, vous n'aurez peut-être aucun plan d'action raisonnable. Il ne serait pas utile de remplacer le calcul plus simple par un calcul qui teste séparément une-1
valeur, et déraisonnable dans la plupart des environnements de versions pour surcharger votre code de tests garantissant que les bibliothèques principales fonctionnent comme prévu. Dans ce cas, les assertions sont idéales.la source
Citation tirée du programmeur pragmatique: du compagnon au maître
la source
Vous devez toujours utiliser la deuxième approche (lever des exceptions).
De plus, si vous êtes en production (et avez une version de build), il est préférable de lever une exception (et de laisser l'application se bloquer dans le pire des cas) que de travailler avec des valeurs non valides et peut-être détruire les données de votre client (ce qui peut coûter des milliers de dollars).
la source
Vous devez utiliser Debug.Assert pour tester les erreurs logiques dans vos programmes. Le compliant ne peut que vous informer des erreurs de syntaxe. Vous devez donc absolument utiliser les instructions Assert pour tester les erreurs logiques. Par exemple, tester un programme qui vend des voitures que seules les BMW bleues devraient bénéficier d'une remise de 15%. Le compliant ne pourrait rien vous dire si votre programme est logiquement correct pour effectuer cela, mais une déclaration assert pourrait le faire.
la source
J'ai lu les réponses ici et j'ai pensé que je devrais ajouter une distinction importante. Il existe deux manières très différentes d'utiliser les assertions. L'une est un raccourci temporaire pour développeur pour «Cela ne devrait pas vraiment se produire, donc s'il me le fait savoir pour que je puisse décider quoi faire», un peu comme un point d'arrêt conditionnel, dans les cas où votre programme peut continuer. L'autre est un moyen de mettre des hypothèses sur les états de programme valides dans votre code.
Dans le premier cas, les assertions n'ont même pas besoin d'être dans le code final. Tu devrais utiliser
Debug.Assert
pendant le développement et vous pouvez les supprimer si / quand vous n'en avez plus besoin. Si vous voulez les laisser ou si vous oubliez de les supprimer, aucun problème, car ils n'auront aucune conséquence dans les compilations Release.Mais dans le second cas, les assertions font partie du code. Ils affirment que vos hypothèses sont vraies et les documentent également. Dans ce cas, vous voulez vraiment les laisser dans le code. Si le programme est dans un état non valide, il ne doit pas être autorisé à continuer. Si vous ne pouviez pas vous permettre d'atteindre les performances, vous n'utiliseriez pas C #. D'une part, il peut être utile de pouvoir attacher un débogueur si cela se produit. De l'autre, vous ne voulez pas que la trace de pile apparaisse sur vos utilisateurs et peut-être plus important que vous ne vouliez pas qu'ils puissent l'ignorer. De plus, s'il est dans un service, il sera toujours ignoré. Par conséquent, en production, le comportement correct serait de lever une exception et d'utiliser la gestion normale des exceptions de votre programme, ce qui pourrait montrer à l'utilisateur un joli message et enregistrer les détails.
Trace.Assert
a le moyen idéal pour y parvenir. Il ne sera pas supprimé en production et peut être configuré avec différents écouteurs à l'aide de app.config. Ainsi, pour le développement, le gestionnaire par défaut est correct, et pour la production, vous pouvez créer un TraceListener simple comme ci-dessous qui lève une exception et l'activer dans le fichier de configuration de production.Et dans le fichier de configuration de production:
la source
Je ne sais pas comment c'est en C # et .NET, mais en C, assert () ne fonctionnera que s'il est compilé avec -DDEBUG - l'utilisateur final ne verra jamais un assert () s'il est compilé sans. C'est uniquement pour les développeurs. Je l'utilise très souvent, il est parfois plus facile de suivre les bugs.
la source
Je ne les utiliserais pas dans le code de production. Jetez les exceptions, attrapez et connectez.
Vous devez également être prudent dans asp.net, car une assertion peut apparaître sur la console et geler la ou les demandes.
la source