Je suis un grand fan de rédiger des assertions, des contrats ou tout autre type de chèques disponibles dans la langue que j'utilise. Une chose qui me dérange un peu est que je ne sais pas quelle est la pratique courante pour traiter les chèques en double.
Exemple de situation: j'écris d'abord la fonction suivante
void DoSomething( object obj )
{
Contract.Requires<ArgumentNullException>( obj != null );
//code using obj
}
puis quelques heures plus tard j'écris une autre fonction qui appelle la première. Comme tout est encore frais en mémoire, je décide de ne pas dupliquer le contrat, car je sais que je DoSomething
vérifierai déjà un objet nul:
void DoSomethingElse( object obj )
{
//no Requires here: DoSomething will do that already
DoSomething( obj );
//code using obj
}
Le problème évident: DoSomethingElse
dépend maintenant DoSomething
de la vérification que obj n'est pas nul. Donc DoSomething
, je devrais jamais décider de ne plus vérifier, ou si je décide d'utiliser une autre fonction, obj pourrait ne plus être vérifié. Ce qui m'amène à écrire cette implémentation après tout:
void DoSomethingElse( object obj )
{
Contract.Requires<ArgumentNullException>( obj != null );
DoSomething( obj );
//code using obj
}
Toujours en sécurité, pas de soucis, sauf que si la situation se développe, le même objet peut être vérifié plusieurs fois et c'est une forme de duplication et nous savons tous que ce n'est pas si bon.
Quelle est la pratique la plus courante pour de telles situations?
ArgumentBullException
? C'est un nouveau :)Réponses:
Personnellement, je vérifierais null dans n'importe quelle fonction qui échouera si elle obtient un null, et pas dans une fonction qui ne le fera pas.
Donc, dans votre exemple ci-dessus, si doSomethingElse () n'a pas besoin de déréférencer obj, je ne vérifierais pas obj pour null.
Si DoSomething () fait déréférencer obj, il doit vérifier la valeur null.
Si les deux fonctions le déréférencent, elles doivent toutes les deux vérifier. Donc, si DoSomethingElse déréférence obj, il doit vérifier la valeur null, mais DoSomething doit également vérifier la valeur null car il peut être appelé à partir d'un autre chemin.
De cette façon, vous pouvez laisser le code assez propre et toujours garantir que les contrôles sont au bon endroit.
la source
DoSomething()
telle sorte que la condition préalable ne soit plus requise (peu probable dans ce cas particulier, mais pourrait se produire dans une situation différente), et supprimez la vérification de la condition préalable. Maintenant, une méthode apparemment totalement indépendante est rompue en raison de la condition préalable manquante. Je vais prendre un peu de duplication de code pour plus de clarté sur des échecs étranges comme celui d'un désir d'enregistrer quelques lignes de code, n'importe quel jour.Génial! Je constate que vous avez découvert les contrats de code pour .NET. Les contrats de code vont beaucoup plus loin que vos assertions moyennes, dont le vérificateur statique est le meilleur exemple. Cela peut ne pas être disponible si vous n'avez pas installé Visual Studio Premium ou une version supérieure, mais il est important de comprendre l'intention derrière cela si vous allez utiliser des contrats de code.
Lorsque vous appliquez un contrat à une fonction, il s'agit littéralement d' un contrat . Cette fonction garantit un comportement conforme au contrat et ne peut être utilisée que conformément à la définition du contrat.
Dans votre exemple donné, la
DoSomethingElse()
fonction n'est pas à la hauteur du contrat spécifié parDoSomething()
, car null peut être transmis, et le vérificateur statique indiquera ce problème. La façon de résoudre ce problème consiste à ajouter le même contrat àDoSomethingElse()
.Maintenant, cela signifie qu'il y aura duplication, mais cette duplication est nécessaire lorsque vous choisissez d' exposer la fonctionnalité à travers deux fonctions. Ces fonctions, bien que privées, peuvent également être appelées à différents endroits de votre classe, donc la seule façon de garantir que l'argument ne sera jamais nul à partir d'un appel donné est de dupliquer les contrats.
Cela devrait vous faire reconsidérer pourquoi vous avez divisé le comportement en deux fonctions en premier lieu. J'ai toujours pensé ( contrairement à la croyance populaire ) que vous ne devriez pas diviser les fonctions qui ne sont appelées qu'à partir d'un seul endroit . En exposant l'encapsulation en appliquant les contrats, cela devient encore plus évident. Il semble que j'ai trouvé une argumentation supplémentaire pour ma cause! Je vous remercie! :)
la source