Pourquoi déclarer des variables proches de l'endroit où elles sont utilisées?


10

J'ai entendu des gens dire que les variables devraient être déclarées aussi près de leur utilisation que possible. Je ne comprends pas ça.

Par exemple, cette politique suggère que je devrais faire ceci:

foreach (var item in veryLongList) {
  int whereShouldIBeDeclared = item.Id;
  //...
}

Mais cela signifie sûrement que les frais généraux de création d'un nouveau intsont engagés à chaque itération. Ne serait-il pas préférable d'utiliser:

int whereShouldIBeDeclared;
foreach (var item in veryLongList) {
  whereShouldIBeDeclared = item.Id;
  //...
}

S'il vous plaît quelqu'un pourrait-il expliquer?


3
Ce serait un langage assez pauvre qui traiterait ces deux cas différemment.
Paul Tomblin

5
Vous partez d'une fausse prémisse. Veuillez voir ma réponse à cette question: stackoverflow.com/questions/6919655/…
CesarGon

8
Si vous pensez comme ça, vous n'êtes pas apte à optimiser ou même à considérer les impacts sur les performances en général. Les implémentations de langage sont intelligentes, et si vous pensez qu'elles ne le sont pas, prouvez-le avec des données fiables obtenues grâce à des repères impartiaux et réalistes.

4
Si les deux exemples de code ont une différence sémantique significative, ils font des choses différentes. Vous devez utiliser celui qui fait ce que vous voulez faire. La règle sur l'endroit où vous déclarez des variables s'applique uniquement aux cas où cela ne fait aucune différence sémantique.
David Schwartz

4
Considérez l'extrémité opposée de l'échelle - tout étant une variable globale. Une «quasi-utilisation déclarée» est-elle certainement la meilleure extrémité de ce spectre?
JBRWilkinson

Réponses:


27

Il s'agit d'une règle de style parmi tant d'autres, et ce n'est pas nécessairement la règle la plus importante de toutes les règles possibles que vous pourriez considérer. Votre exemple, car il inclut un int, n'est pas super convaincant, mais vous pourriez certainement avoir un objet coûteux à construire à l'intérieur de cette boucle, et peut-être un bon argument pour construire l'objet en dehors de la boucle. Cependant, cela ne fait pas un bon argument contre cette règle car, d'abord, il y a des tonnes d'autres endroits qu'il pourrait appliquer qui n'impliquent pas la construction d'objets coûteux en boucle, et deuxièmement, un bon optimiseur (et vous avez étiqueté C #, donc vous avez un bon optimiseur) peut hisser l'initialisation hors de la boucle.

La vraie raison de cette règle est aussi la raison pour laquelle vous ne voyez pas pourquoi c'est une règle. Les gens écrivaient des fonctions qui faisaient des centaines, voire des milliers de lignes et ils les écrivaient dans des éditeurs de texte brut (pensez au Bloc-notes) sans le type de support fourni par Visual Studio. Dans cet environnement, déclarer une variable à des centaines de lignes de l'endroit où elle était utilisée signifiait que la personne qui lisait

if (flag) limit += factor;

n'avait pas beaucoup d'indices sur le drapeau, la limite et le facteur. Des conventions de dénomination comme la notation hongroise ont été adoptées pour aider à cela, tout comme des règles comme déclarer des choses près de l'endroit où elles sont utilisées. Bien sûr, de nos jours, il s'agit de refactoring, et les fonctions sont généralement inférieures à une page, ce qui rend difficile la distance entre l'endroit où les choses sont déclarées et où elles sont utilisées. Vous opérez dans une plage de 0 à 20 et vous dites que peut-être 7 est correct dans ce cas particulier, tandis que le gars qui a établi la règle aurait ADORÉ obtenir 7 lignes et essayait de parler à quelqu'un à partir de 700. Et sur en plus de cela, dans Visual Studio, vous pouvez passer la souris sur n'importe quoi et voir son type, est-ce une variable membre, etc. Cela signifie que le besoin de voir la ligne le déclarer est diminué.

C'est toujours une règle assez bonne, une règle qui est en fait assez difficile à briser ces jours-ci, et que personne n'a jamais défendue comme raison d'écrire du code lent. Soyez sensible avant tout.


Merci pour votre réponse. Mais sûrement, quel que soit le type de données, une nouvelle instance est créée à chaque itération, quelle que soit la façon dont je le fais? C'est juste que dans le deuxième cas, nous ne demandons pas une nouvelle référence de mémoire à chaque fois. Ou ai-je manqué le point? Et vous dites que l'optimiseur C # améliorera automatiquement mon code lors de sa compilation? Je ne le savais pas!
James

2
La surcharge de création d'un int est minuscule. Si vous construisiez quelque chose de compliqué, les frais généraux seraient plus importants.
Kate Gregory

17
Il ne s'agit pas seulement de voir son type et autres. C'est aussi une question de vie. Si la variable "wibble" est déclarée 30 lignes avant sa première utilisation, il y a 30 lignes dans lesquelles une utilisation erronée de "wibble" peut entraîner un bug. Si elle est déclarée immédiatement avant son utilisation, l'utilisation de "wibble" dans ces 30 lignes précédentes n'entraînera pas de bogue. Cela provoquera une erreur de compilation à la place.
Mike Sherrill 'Cat Recall'

Dans ce cas, une nouvelle instance n'est pas créée à chaque boucle. Une seule variable de niveau supérieur est créée et utilisée pour chaque itération (regardez l'IL). Mais c'est un détail d'implémentation.
thecoop

"dans Visual Studio, vous pouvez passer la souris sur tout et voir" etc. Il y a aussi Naviguer vers la définition, qui a le raccourci F12qui est indispensable.
StuperUser

15

La définition de la variable à l'intérieur de la boucle rend la visibilité locale à cette boucle uniquement. Cela présente au moins 3 avantages pour le lecteur:

  1. La définition de la variable et tous les commentaires associés sont faciles à trouver
  2. Le lecteur sait que cette variable n'est jamais utilisée ailleurs (aucune dépendance à attendre)
  3. Lorsque le code est écrit ou édité, il n'y a aucune chance que vous puissiez utiliser le même nom de variable en dehors de la boucle pour faire référence à cette variable sinon, vous pourriez obtenir une erreur.

Quant au bit d'efficacité, le compilateur est intelligent pour générer la définition en dehors de la boucle dans le code optimisé généré. La variable ne sera pas créée à chaque itération de boucle.


4

Les gens disent aussi près de leur utilisation que possible , ils ne disent pas que vous devriez avoir à le faire tout le temps, car il y a des cas où déclarer des variables dans le moins de portée entraînera des frais généraux. Les principales raisons de cette déclaration sont la lisibilité et donner des variables la plus petite portée possible.


4

Bien qu'elle aide à la lisibilité, la lisibilité n'est pas la considération principale dans ce cas, et les IDE modernes n'éliminent pas la nécessité de cette règle.

La principale préoccupation concerne les variables non initialisées. Si vous déclarez une variable trop éloignée de son initialisation, elle vous ouvre à toutes sortes de problèmes potentiels. Vous pourriez vous retrouver accidentellement à travailler avec tout ce qui se trouvait auparavant dans la RAM, ou le résultat d'un calcul plus élevé dans la fonction, ou une initialisation fictive (comme 0) que quelqu'un a mise juste pour empêcher le compilateur de se plaindre. Les gens insèreront du code entre votre déclaration et votre utilisation sans être au courant de vos conditions préalables implicites pour cette variable. Dans le pire des cas, cette utilisation fonctionnera simplement dans vos tests mais échouera sur le terrain.

Déclarer vos variables dans une portée aussi petite que possible et les initialiser à une valeur appropriée juste au moment de la déclaration évitera de nombreux maux de tête de maintenance. Le fait qu'il oblige à une meilleure lisibilité n'est qu'un bel effet secondaire.


1

Ce n'est pas un "must". C'est juste une opinion, je dois faire quelque chose. Par exemple, j'aime déclarer tous les vars dans les premières lignes de la méthode afin que je puisse commenter ce que je vais faire avec ces vars (bien sûr à moins qu'ils ne soient des compteurs). D'autres personnes, comme vous l'avez entendu, aiment les placer le plus près possible de leur utilisation (comme dans le deuxième exemple que vous avez écrit). Quoi qu'il en soit, le premier exemple que vous fournissez est sûrement une "erreur" (dans le sens où cela entraînera une surcharge comme vous le comprenez).

Il vous suffit de choisir votre chemin et de le suivre.


2
Ce n'est pas seulement une opinion, n'est-ce pas? La recherche en génie logiciel n'a-t-elle pas documenté la relation entre le temps réel et le nombre de bogues depuis au moins les années 80?
Mike Sherrill «Cat Recall»

1

Vos deux exemples sont du code fonctionnellement différent, ils ne sont pas interchangeables. (Vos exemples dépouillés laissent une distinction sans différence, mais dans un code non trivial, cela fait une différence). La règle que vous visitez est toujours subordonnée à des considérations de portée, comme indiqué par "... que possible".

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.