Imaginez le code suivant:
void DoThis()
{
if (!isValid) return;
DoThat();
}
void DoThat() {
Console.WriteLine("DoThat()");
}
Est-il acceptable d'utiliser un retour dans une méthode void? Y a-t-il une pénalité de performance? Ou il serait préférable d'écrire un code comme celui-ci:
void DoThis()
{
if (isValid)
{
DoThat();
}
}
Réponses:
Un retour dans une méthode void n'est pas mauvais, est une pratique courante pour inverser les
if
instructions pour réduire l'imbrication .Et avoir moins d'imbrication sur vos méthodes améliore la lisibilité et la maintenabilité du code.
En fait, si vous avez une méthode void sans aucune instruction return, le compilateur générera toujours une instruction ret à la fin de celle-ci.
la source
Il y a une autre bonne raison d'utiliser des gardes (par opposition au code imbriqué): si un autre programmeur ajoute du code à votre fonction, il travaille dans un environnement plus sûr.
Considérer:
void MyFunc(object obj) { if (obj != null) { obj.DoSomething(); } }
contre:
void MyFunc(object obj) { if (obj == null) return; obj.DoSomething(); }
Maintenant, imaginez qu'un autre programmeur ajoute la ligne: obj.DoSomethingElse ();
void MyFunc(object obj) { if (obj != null) { obj.DoSomething(); } obj.DoSomethingElse(); } void MyFunc(object obj) { if (obj == null) return; obj.DoSomething(); obj.DoSomethingElse(); }
C'est évidemment un cas simpliste, mais le programmeur a ajouté un crash au programme dans la première instance (code imbriqué). Dans le deuxième exemple (sortie anticipée avec des gardes), une fois que vous avez dépassé la garde, votre code est à l'abri de l'utilisation involontaire d'une référence nulle.
Bien sûr, un grand programmeur ne fait pas d'erreurs comme celle-ci (souvent). Mais mieux vaut prévenir que guérir - nous pouvons écrire le code de manière à éliminer complètement cette source potentielle d'erreurs. L'imbrication ajoute de la complexité. Les meilleures pratiques recommandent donc de refactoriser le code pour réduire l'imbrication.
la source
Mauvaise pratique ??? En aucune façon. En fait, il est toujours préférable de gérer les validations en revenant de la méthode au plus tôt si les validations échouent. Sinon, cela entraînerait une énorme quantité de ifs & autres imbriqués. La résiliation anticipée améliore la lisibilité du code.
Vérifiez également les réponses à une question similaire: Dois-je utiliser l'instruction return / continue au lieu de if-else?
la source
Ce n'est pas une mauvaise pratique (pour toutes les raisons déjà évoquées). Cependant, plus vous avez de retours dans une méthode, plus elle devrait être divisée en méthodes logiques plus petites.
la source
Le premier exemple utilise une instruction de garde. De Wikipedia :
Je pense qu'avoir un groupe de gardes au sommet d'une méthode est une façon parfaitement compréhensible de programmer. Il dit essentiellement "n'exécutez pas cette méthode si l'une de celles-ci est vraie".
Donc en général, il aimerait ceci:
void DoThis() { if (guard1) return; if (guard2) return; ... if (guardN) return; DoThat(); }
Je pense que c'est beaucoup plus lisible alors:
void DoThis() { if (guard1 && guard2 && guard3) { DoThat(); } }
la source
Il n'y a aucune pénalité de performance, cependant le deuxième morceau de code est plus lisible et donc plus facile à maintenir.
la source
Dans ce cas, votre deuxième exemple est un meilleur code, mais cela n'a rien à voir avec le retour d'une fonction void, c'est simplement parce que le deuxième code est plus direct. Mais revenir d'une fonction vide est tout à fait acceptable.
la source
C'est parfaitement correct et pas de «pénalité de performance», mais n'écrivez jamais une instruction «si» sans crochets.
Toujours
if( foo ){ return; }
C'est beaucoup plus lisible; et vous ne supposerez jamais accidentellement que certaines parties du code se trouvent dans cette instruction alors qu'elles ne le sont pas.
la source
{
. Cela vous aligne{
avec votre}
dans la même colonne, ce qui facilite grandement la lisibilité (beaucoup plus facile de trouver les accolades ouvertes / fermées correspondantes).if
sera alors facile de distinguer visuellement quelles instructions nécessitent des accolades fermées, et ainsi avoir desif
instructions contrôlant une seule instruction sera sûr. Le fait de repousser l'accolade ouverte sur la ligne avec leif
enregistre une ligne d'espace vertical sur chaque instruction multipleif
, mais nécessitera l'utilisation d'une ligne d'accolade fermée autrement inutile.Je ne suis pas d'accord avec vous tous les jeunes whippersnappers sur celui-ci.
Utiliser le retour au milieu d'une méthode, nulle ou non, est une très mauvaise pratique, pour des raisons qui ont été énoncées assez clairement, il y a près de quarante ans, par le regretté Edsger W. Dijkstra, à partir de la célèbre déclaration GOTO considérée comme nuisible ", et continuant dans" Programmation structurée ", par Dahl, Dijkstra et Hoare.
La règle de base est que chaque structure de contrôle et chaque module doivent avoir exactement une entrée et une sortie. Un retour explicite au milieu du module enfreint cette règle et rend beaucoup plus difficile de raisonner sur l'état du programme, ce qui à son tour rend beaucoup plus difficile de dire si le programme est correct ou non (ce qui est une propriété beaucoup plus forte que "que cela semble fonctionner ou non").
La «déclaration GOTO considérée comme nuisible» et la «programmation structurée» ont lancé la révolution de la «programmation structurée» des années 1970. Ces deux éléments sont les raisons pour lesquelles nous avons aujourd'hui if-then-else, while-do et d'autres constructions de contrôle explicites, et pourquoi les déclarations GOTO dans des langages de haut niveau sont sur la liste des espèces en danger. (Mon opinion personnelle est qu'elles doivent figurer sur la liste des espèces disparues.)
Il convient de noter que Message Flow Modulator, le premier logiciel militaire qui a JAMAIS passé les tests d'acceptation du premier essai, sans écarts, dérogations ou verbiage "ouais, mais", a été écrit dans une langue qui n'avait même pas une instruction GOTO.
Il convient également de mentionner que Nicklaus Wirth a changé la sémantique de l'instruction RETURN dans Oberon-07, la dernière version du langage de programmation Oberon, ce qui en fait un élément final de la déclaration d'une procédure typée (c'est-à-dire une fonction), plutôt qu'un instruction exécutable dans le corps de la fonction. Son explication du changement dit qu'il l'a fait précisément parce que le formulaire précédent était une violation du principe à une sortie de la programmation structurée.
la source
Lorsque vous utilisez des gardes, assurez-vous de suivre certaines directives pour ne pas confondre les lecteurs.
Exemple
// guards point you to the core intent void Remove(RayCastResult rayHit){ if(rayHit== RayCastResult.Empty) return ; rayHit.Collider.Parent.Remove(); } // no guards needed: function split into multiple cases int WonOrLostMoney(int flaw)=> flaw==0 ? 100 : flaw<10 ? 30 : flaw<20 ? 0 : -20 ;
la source
Lancer une exception au lieu de ne rien renvoyer lorsque l'objet est nul, etc.
Votre méthode s'attend à ce que l'objet ne soit pas nul et n'est pas le cas, vous devez donc lever une exception et laisser l'appelant gérer cela.
Mais le retour anticipé n'est pas une mauvaise pratique autrement.
la source