Por que declarar variáveis ​​próximas de onde elas são usadas?

10

Ouvi pessoas dizerem que variáveis ​​devem ser declaradas o mais próximo possível de seu uso. Eu não entendo isso.

Por exemplo, esta política sugere que eu faça isso:

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

Mas certamente isso significa que as despesas gerais da criação de um novo intsão incorridas a cada iteração. Não seria melhor usar:

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

Por favor, alguém poderia explicar?

James
fonte
3
Seria uma linguagem muito ruim que tratasse esses dois casos de maneira diferente.
Paul Tomblin
5
Você é a partir de uma premissa falsa. Por favor, veja a minha resposta a esta pergunta: stackoverflow.com/questions/6919655/...
CesarGon
8
Se você pensa assim, não está apto para otimizar ou mesmo considerar os impactos no desempenho em geral. As implementações de idiomas são inteligentes e, se você acha que não, prove com dados concretos obtidos por meio de benchmarks imparciais e realistas.
4
Se os dois exemplos de código tiverem uma diferença semântica significativa, eles farão coisas diferentes. Você deve usar o que faz o que deseja fazer. A regra sobre onde você declara variáveis ​​se aplica apenas aos casos em que não faz diferença semântica.
David Schwartz
4
Considere o extremo oposto da escala - tudo sendo uma variável global. Certamente 'declarado próximo ao uso' é o melhor final desse espectro?
JBRWilkinson

Respostas:

27

Essa é uma regra de estilo entre muitas, e não é necessariamente a regra mais importante de todas as regras possíveis que você pode considerar. Seu exemplo, uma vez que inclui um int, não é super atraente, mas você certamente pode ter um objeto caro de construir dentro desse loop e talvez um bom argumento para construir o objeto fora do loop. No entanto, isso não torna um bom argumento contra essa regra, pois primeiro, existem muitos outros lugares que podem ser aplicados que não envolvem a construção de objetos caros em um loop e, segundo, um bom otimizador (e você marcou C #, para que você tenha um bom otimizador) pode elevar a inicialização fora do loop.

A verdadeira razão para esta regra também é a razão pela qual você não vê por que é uma regra. As pessoas costumavam escrever funções com centenas, até milhares de linhas, e escreviam em editores de texto sem formatação (pense no Bloco de Notas) sem o tipo de suporte fornecido pelo Visual Studio. Nesse ambiente, declarar uma variável a centenas de linhas de distância de onde foi usada significava que a pessoa que estava lendo

if (flag) limit += factor;

não tinha muitas pistas sobre o que eram bandeira, limite e fator. Convenções de nomenclatura como notação húngara foram adotadas para ajudar com isso, assim como regras como declarar coisas próximas de onde elas são usadas. É claro que atualmente, trata-se de refatoração, e as funções geralmente têm menos de uma página, dificultando a distância entre o local em que as coisas são declaradas e o local onde são usadas. Você está operando em um intervalo de 0 a 20 e discutindo que talvez 7 esteja ok nesse caso em particular, enquanto o cara que fez a regra adoraria tirar 7 linhas de distância e estava tentando convencer alguém de 700. E assim por diante Além disso, no Visual Studio, você pode passar o mouse sobre qualquer coisa e ver seu tipo, é uma variável de membro e assim por diante. Isso significa que a necessidade de ver a linha declarando que é diminuída.

Ainda é uma regra razoavelmente boa, uma que é realmente muito difícil de quebrar hoje em dia e que ninguém jamais defendeu como razão para escrever código lento. Seja sensato, acima de tudo.

Kate Gregory
fonte
Obrigado pela sua resposta. Mas, certamente, independentemente do tipo de dados, uma nova instância é criada em cada iteração, da maneira que eu faço isso? É apenas que, no segundo caso, não solicitamos uma nova referência de memória a cada vez. Ou eu perdi o ponto? E você está dizendo que o otimizador de C # melhorará automaticamente meu código quando for compilado? Eu não sabia disso!
James
2
A sobrecarga de criar um int é pequena. Se você estivesse construindo algo complicado, a sobrecarga seria um negócio maior.
Kate Gregory
17
Não é apenas uma questão de poder ver seu tipo e tal. É também uma questão de vida. Se a variável "wibble" for declarada 30 linhas antes de ser usada pela primeira vez, existem 30 linhas nas quais o uso incorreto de "wibble" pode resultar em um bug. Se for declarado imediatamente antes de ser usado, o uso de "wibble" nessas 30 linhas anteriores não resultará em erro. Isso causará um erro no compilador.
Mike Sherrill 'Cat Recall'
Nesse caso, uma nova instância não é criada a cada loop. Uma única variável de nível superior é criada e usada para cada iteração (veja o IL). Mas isso é um detalhe de implementação.
Thecoop 10/10
"no Visual Studio, você pode passar o mouse sobre qualquer coisa e ver" etc. Também há Navegar para a definição, que possui o atalho F12indispensável.
StuperUser
15

Definir a variável dentro do loop torna a visibilidade local apenas para esse loop. Isso tem pelo menos três vantagens para o leitor:

  1. A definição da variável e quaisquer comentários relacionados são fáceis de encontrar
  2. O leitor sabe que essa variável nunca é usada em nenhum outro lugar (sem dependência de esperar)
  3. Quando o código é escrito ou editado, não há chance de que você possa usar o mesmo nome de variável fora do loop para se referir a essa variável, caso contrário, poderá ocorrer um erro.

Quanto ao bit de eficiência, o compilador é inteligente para gerar a definição fora do loop no código otimizado gerado. A variável não será criada a cada iteração de loop.

NoChance
fonte
4

As pessoas dizem que o mais próximo possível de seu uso , elas não estão dizendo que você deveria fazer isso o tempo todo, porque são alguns casos em que declarar variáveis ​​no menor escopo causará alguma sobrecarga. Os principais motivos dessa declaração são Legibilidade e indicação de variáveis. o menor escopo possível.

invariante
fonte
4

Embora ajude com a legibilidade, a legibilidade não é a principal consideração nesse caso, e os IDEs modernos não evitam a necessidade dessa regra.

A principal preocupação são as variáveis ​​não inicializadas. Se você declarar uma variável muito distante de sua inicialização, ela abrirá todos os tipos de problemas em potencial. Você pode encontrar-se acidentalmente trabalhando com o que aconteceu antes na RAM, ou o resultado de um cálculo mais alto na função ou uma inicialização fictícia (como 0) que alguém colocou apenas para impedir que o compilador se queixe. As pessoas inserirão código entre a sua declaração e o uso sem estar ciente de suas pré-condições implícitas para essa variável. Nos piores casos, esse uso funcionará nos seus testes, mas falhará no campo.

Declarar suas variáveis ​​no menor escopo possível e inicializá-las com um valor adequado no ponto da declaração evitará muitas dores de cabeça de manutenção. O fato de compelir a legibilidade aprimorada é apenas um bom efeito colateral.

Karl Bielefeldt
fonte
1

Não é um "must". É apenas uma opinião, eu maneira de fazer alguma coisa. Por exemplo, eu gosto de declarar todos os vars nas primeiras linhas do método para poder comentar o que farei com esses vars (é claro, a menos que sejam contadores). Outras pessoas, como você ouviu, gostam de colocá-las o mais próximo possível de seu uso (como no segundo exemplo que você escreveu). De qualquer forma, o primeiro exemplo que você fornece é certamente um "erro" (no sentido em que causará uma sobrecarga conforme você entende).

Você precisa simplesmente escolher o seu caminho e segui-lo.

Aurelio De Rosa
fonte
2
Não é apenas uma opinião, é? A pesquisa de engenharia de software não documentou a relação entre tempo ativo e número de bugs desde pelo menos os anos 80?
Mike Sherrill 'Cat Recall'
1

Seus dois exemplos são códigos funcionalmente diferentes, não são intercambiáveis. (Seus exemplos simplificados deixam uma distinção sem diferença, mas no código não trivial faz diferença). A regra que você site está sempre subordinada a considerações de escopo, conforme indicado por "... quanto possível".

kylben
fonte