Debug.Assert vs Exception Lancer

J’ai lu de nombreux articles (et quelques autres questions similaires qui ont été publiées sur StackOverflow) sur comment et quand utiliser des assertions, et je les ai bien compris. Mais malgré tout, je ne comprends pas quel type de motivation devrait me pousser à utiliser Debug.Assert au lieu de lancer une simple exception. Ce que je veux dire par là, dans .NET, la réponse par défaut à une assertion échouée consiste à “arrêter le monde” et à afficher une boîte de message pour l’utilisateur. Bien que ce type de comportement puisse être modifié, je trouve cela très ennuyeux et redondant de le faire, alors que je pourrais au lieu de cela, lancer une exception appropriée. De cette façon, je pourrais facilement écrire l’erreur dans le journal de l’application juste avant que je lance l’exception, et en plus, mon application ne gèle pas nécessairement.

Alors, pourquoi devrais-je utiliser Debug.Assert au lieu d’une simple exception? Placer une assertion là où ça ne devrait pas être peut causer toutes sortes de “comportements indésirables”, donc à mon sharepoint vue, je ne gagne rien en utilisant une assertion au lieu de lancer une exception. Êtes-vous d’accord avec moi ou est-ce que je manque quelque chose ici?

Note: Je comprends parfaitement la différence “en théorie” (Debug vs Release, modes d’utilisation, etc.), mais à mon avis, il serait préférable de lancer une exception au lieu d’effectuer une assertion. Étant donné que si un bogue est découvert sur une version de production, je souhaiterais quand même que “l’assertion” échoue (après tout, le “surcoût” est ridiculement petit), donc je ferais mieux de lancer une exception à la place.


Edit: La façon dont je le vois, si une assertion a échoué, cela signifie que l’application est entrée dans une sorte d’état corrompu et inattendu. Alors, pourquoi voudrais-je continuer l’exécution? Peu importe que l’application s’exécute sur une version de débogage ou de version. La même chose vaut pour les deux

Bien que je sois d’accord sur le fait que votre raisonnement soit plausible , c’est-à-dire si une affirmation est violée de manière inattendue, il est logique d’arrêter l’exécution en lançant. Voici pourquoi:

Comme d’autres l’ont dit, les assertions devraient documenter des situations impossibles , de telle sorte que si la situation prétendument impossible se réalise, le développeur en soit informé. Les exceptions, en revanche, fournissent un mécanisme de contrôle des stream pour des situations exceptionnelles, improbables ou erronées, mais pas des situations impossibles. Pour moi, la principale différence est la suivante:

  • Il devrait TOUJOURS être possible de produire un test élémentaire qui exerce une déclaration de lancer donnée. S’il n’est pas possible de produire un tel cas de test, vous avez un chemin de code dans votre programme qui ne s’exécute jamais et il doit être supprimé en tant que code mort.

  • Il ne devrait JAMAIS être possible de produire un test élémentaire qui provoque une assertion. Si une assertion se déclenche, le code est erroné ou l’assertion est incorrecte. de toute façon, quelque chose doit changer dans le code.

C’est pourquoi je ne remplacerais pas une assertion par une exception. Si l’assertion ne peut pas être lancée, le remplacer par une exception signifie que votre programme contient un chemin de code non testable . Je n’aime pas les chemins de code non testables.

Les assertions permettent de vérifier la compréhension du monde par le programmeur. Une assertion ne devrait échouer que si le programmeur a fait quelque chose de mal. Par exemple, n’utilisez jamais une assertion pour vérifier les entrées de l’utilisateur.

Assure un test pour des conditions qui “ne peuvent pas arriver”. Les exceptions concernent les conditions qui “ne devraient pas se produire mais le font”.

Les assertions sont utiles car, au moment de la construction (ou même de l’exécution), vous pouvez modifier leur comportement. Par exemple, souvent dans les versions de publication, les assertions ne sont même pas vérifiées, car elles entraînent une surcharge inutile. C’est aussi quelque chose dont il faut se méfier: vos tests peuvent même ne pas être exécutés.

Si vous utilisez des exceptions au lieu d’assertions, vous perdez de la valeur:

  1. Le code est plus verbeux, car le fait de tester et de lancer une exception comporte au moins deux lignes, alors qu’une assertion en est une seule.

  2. Votre code de test et de lancement sera toujours exécuté, tandis que les assertions pourront être compilées.

  3. Vous perdez certaines communications avec d’autres développeurs, car les assertions ont une signification différente de celle du code produit qui vérifie et lance. Si vous testez réellement une assertion de programmation, utilisez un assert.

Plus ici: http://nedbatchelder.com/text/assert.html

EDIT: En réponse à la modification / note que vous avez faite dans votre post: Il semble que l’utilisation des exceptions est la bonne chose à utiliser en utilisant des assertions pour le type de choses que vous essayez d’accomplir. Je pense que la pierre d’achoppement mentale que vous frappez est que vous considérez que les exceptions et les affirmations remplissent le même objective, et vous essayez donc de déterminer laquelle serait «correcte» à utiliser. Bien qu’il puisse y avoir un certain chevauchement dans la manière dont les assertions et les exceptions peuvent être utilisées, ne confondez pas le fait que, pour elles, les solutions différentes au même problème ne le sont pas. Les assertions et les exceptions ont chacune leur objective, leurs forces et leurs faiblesses.

J’allais taper une réponse dans mes propres mots mais ceci rend le concept plus juste que ce que j’aurais:

C # Station: Assertions

L’utilisation d’instructions assert peut être un moyen efficace de détecter les erreurs de logique de programme au moment de l’exécution, mais elles sont facilement éliminées du code de production. Une fois le développement terminé, le coût d’exécution de ces tests redondants pour les erreurs de codage peut être éliminé simplement en définissant le symbole de préprocesseur NDEBUG [qui désactive toutes les assertions] lors de la compilation. Veillez toutefois à vous rappeler que le code placé dans l’assertion elle-même sera omis dans la version de production.

Une assertion permet de tester une condition uniquement lorsque toutes les conditions suivantes sont remplies:

 * the condition should never be false if the code is correct, * the condition is not so sortingvial so as to obviously be always true, and * the condition is in some sense internal to a body of software. 

Les assertions ne devraient presque jamais être utilisées pour détecter des situations qui surviennent pendant le fonctionnement normal du logiciel. Par exemple, les assertions ne doivent généralement pas être utilisées pour rechercher des erreurs dans les entrées d’un utilisateur. Il peut toutefois être judicieux d’utiliser des assertions pour vérifier qu’un appelant a déjà vérifié les entrées d’un utilisateur.

Fondamentalement, utilisez des exceptions pour les choses qui doivent être interceptées / traitées dans une application de production, utilisez des assertions pour effectuer des vérifications logiques qui seront utiles pour le développement mais désactivées en production.

Je pense qu’un exemple pratique (artificiel) peut aider à éclairer la différence:

(adapté de l’ extension de lot MoreLinq )

 // 'public facing' method public int DoSomething(List stuff, object doohickey, int limit) { // validate user input and report problems externally with exceptions if(stuff == null) throw new ArgumentNullException("stuff"); if(doohickey == null) throw new ArgumentNullException("doohickey"); if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0"); return DoSomethingImpl(stuff, doohickey, limit); } // 'developer only' method private static int DoSomethingImpl(List stuff, object doohickey, int limit) { // validate input that should only come from other programming methods // which we have control over (eg we already validated user input in // the calling method above), so anything using this method shouldn't // need to report problems externally, and compilation mode can remove // this "unnecessary" check from production Debug.Assert(stuff != null); Debug.Assert(doohickey != null); Debug.Assert(limit > 0); /* now do the actual work... */ } 

Comme Eric Lippert et ses collaborateurs l’ont dit, vous n’affirmez que ce que vous pensez être correct, au cas où vous (le développeur) l’ auriez accidentellement utilisé par erreur , vous pouvez donc corriger votre code. En gros, vous lancez des exceptions lorsque vous n’avez pas le contrôle ou ne pouvez pas anticiper ce qui arrive, par exemple pour les entrées utilisateur , de sorte que tout ce qui lui donne des données erronées puisse répondre correctement (par exemple l’utilisateur).

Une autre pépite de Code Complete :

“Une assertion est une fonction ou une macro qui se plaint bruyamment si une hypothèse est fausse. Utilisez des assertions pour documenter des hypothèses faites dans le code et pour vider des conditions inattendues. …

“Pendant le développement, les assertions éliminent les hypothèses contradictoires, les conditions inattendues, les mauvaises valeurs transmises aux routines, etc.”

Il continue en ajoutant quelques lignes direcsortingces sur ce qui devrait et ne devrait pas être affirmé.

En revanche, des exceptions:

“Utilisez la gestion des exceptions pour attirer l’attention sur des cas inattendus. Les cas exceptionnels doivent être traités de manière à les rendre évidents lors du développement et à être récupérables lorsque le code de production est en cours d’exécution.”

Si vous n’avez pas ce livre, vous devriez l’acheter.

Debug.Assert par défaut ne fonctionnera que dans les versions de débogage. Par conséquent, si vous souhaitez détecter tout comportement inattendu dans vos versions, vous devrez utiliser des exceptions ou activer la constante de débogage dans vos propriétés de projet (ce qui est considéré dans général pour ne pas être une bonne idée).

Utilisez des assertions pour des choses qui sont possibles mais ne devraient pas arriver (si c’était impossible, pourquoi poseriez-vous une affirmation?).

Cela ne ressemble-t-il pas à un cas d’utilisation d’une Exception ? Pourquoi utiliseriez-vous une assertion au lieu d’une Exception ?

Parce qu’il devrait y avoir du code appelé avant votre assertion qui empêcherait que le paramètre d’assertion soit faux.

Habituellement, il n’y a pas de code avant votre Exception qui garantit qu’il ne sera pas lancé.

Pourquoi est-il bon que Debug.Assert() soit compilé dans prod? Si vous voulez en savoir plus sur le débogage, ne voudriez-vous pas en prendre connaissance dans prod?

Vous ne le voulez que pendant le développement, car une fois que vous avez trouvé des situations Debug.Assert(false) , vous écrivez ensuite du code pour garantir que Debug.Assert(false) ne se reproduise plus. Une fois le développement terminé, en supposant que vous avez trouvé les situations Debug.Assert(false) et que vous les avez corrigées, Debug.Assert() peut être compilé en toute sécurité car elles sont désormais redondantes.