14

I have heard people say that variables should be declared as close to their usage as possible. I don't understand this.

For example, this policy would suggest I should do this:

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

But surely this means the overheads of creating a new int are incurred on every iteration. Wouldn't it be better to use:

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

Please could somebody explain?

8
  • 4
    It would be a pretty damn poor language that treated those two cases differently. Commented Oct 9, 2011 at 14:14
  • 5
    You a starting from a false premise. Please see my answer to this question: stackoverflow.com/questions/6919655/…
    – CesarGon
    Commented Oct 9, 2011 at 14:23
  • 8
    If you think like that, you're not fit for optimizing or even considering performance impacts in general. Language implementations are smart, and if you think they aren't, prove it with hard data obtained through unbiased, realistic benchmarks.
    – user7043
    Commented Oct 9, 2011 at 14:41
  • 4
    If the two code examples have a significant semantic difference, then they do different things. You should use the one that does what you want to do. The rule about where you declare variables only applies to cases where it makes no semantic difference. Commented Oct 9, 2011 at 15:27
  • 4
    Consider the opposite end of the scale - everything being a global variable. Surely 'declared near usage' is the better end of this spectrum? Commented Oct 10, 2011 at 11:52

6 Answers 6

29

This is one style rule among many, and it isn't necessarily the most important rule of all the possible rules you could consider. Your example, since it includes an int, isn't super compelling, but you could certainly have an expensive-to-construct object inside that loop, and perhaps a good argument for constructing the object outside the loop. However, that doesn't make it a good argument against this rule since first, there are tons of other places it could apply that don't involve constructing expensive objects in a loop, and second, a good optimizer (and you've tagged C#, so you have a good optimizer) can hoist the initialization out of the loop.

The real reason for this rule is also the reason you don't see why it's a rule. People used to write functions that were hundreds, even thousands of lines long and they used to write them in plain text editors (think Notepad) without the kind of support Visual Studio provided. In that environment, declaring a variable hundreds of lines away from where it was used meant that the person reading

if (flag) limit += factor;

didn't have a lot of clues about what flag, limit and factor were. Naming conventions like Hungarian notation were adopted to help with this, and so were rules like declaring things close to where they are used. Of course, these days, it's all about refactoring, and functions are generally less than a page long, making it hard to get very much distance between where things are declared and where they are used. You're operating in a range of 0-20 and quibbling that maybe 7 is ok in this particular instance, while the guy who made the rule would have LOVED to get 7 lines away and was trying to talk someone down from 700. And on top of that, in Visual Studio, you can mouse over anything and see its type, is it a member variable, and so on. That means the need to see the line declaring it is lessened.

It's still a reasonably good rule, one that's actually quite hard to break these days, and one that no-one ever advocated as a reason to write slow code. Be sensible, above all.

6
  • 1
    Thanks for your answer. But surely regardless of the data type, a new instance is created on every iteration whichever way I do it? It's just that in the second case we don't ask for a new memory reference each time. Or have I missed the point? And are you saying that the C# optimizer will automatically improve my code when it compiles anyway? I didn't know that!
    – James
    Commented Oct 9, 2011 at 14:12
  • 3
    The overhead of creating an int is tiny. If you were constructing something complicated, the overhead would be a bigger deal. Commented Oct 9, 2011 at 14:17
  • 22
    It's not only a question of being able to see its type and such. It's also a question of lifetime. If the variable "wibble" is declared 30 lines before it's first used, there are 30 lines in which a mistaken use of "wibble" can result in a bug. If it's declared immediately before it's used, using "wibble" in those 30 previous lines won't result in a bug. It'll cause a compiler error instead. Commented Oct 9, 2011 at 17:57
  • 2
    In this case, a new instance is not created every loop. A single top-level variable is created and used for every iteration (look at the IL). But that's an implementation detail.
    – thecoop
    Commented Oct 10, 2011 at 12:14
  • 1
    "People used to write functions that were hundreds, even thousands of lines long and they used to write them in plain text editors (think Notepad) without the kind of support Visual Studio provided." Ah, the good old days. Thanks for that, lol.
    – Suamere
    Commented Jun 13, 2013 at 20:34
19

Defining the variable inside the loop makes it visibility local to that loop only. This has at least 3 advantages for the reader:

  1. The variable definition and any related comments are easy to find
  2. The reader knows that this variable is never used else where (no dependency to expect)
  3. When the code is written or edited, there is no chance that you could use the same variable name outside the loop to refer to that variable otherwise, you could get in error.

As for the efficiency bit, the compiler is smart to generate the definition outside of the loop in the optimized code generated. The variable will not be created every loop iteration.

7

People say as close to their usage as possible , They are not saying you should have to do that all the time,because they are some cases declaring variables in least scope will cause some overhead .The main reasons of that statement are Readability and giving variables the smallest scope you can .

5

Although it helps with readability, readability isn't the primary consideration in this case, and modern IDEs don't obviate the need for this rule.

The primary concern is uninitialized variables. If you declare a variable too far away from its initialization, it opens you up to all kinds of potential problems. You might find yourself accidentally working with whatever happened to be there in RAM before, or the result from a calculation higher in the function, or a dummy initialization (like 0) that someone put in just to keep the compiler from complaining. People will insert code in between your declaration and usage without being aware of your implicit preconditions for that variable. In the worst cases, that usage will just happen to work in your tests but fail in the field.

Declaring your variables in as small a scope as possible, and initializing them to a proper value right at the point of declaration will avoid a great many maintenance headaches. The fact that it compels improved readability is just a nice side effect.

1

It is not a "must". It's just an opinion, I way to do something. For example, I like to declare all the vars in the first lines of the method so I can comment what I'll do with that vars (of course unless they are counters). Other people, as you heard, like to place them as close to their usage as possible (as in the second example you wrote). Anyway, the first example you provide is surely an "error" (in the sense that it will cause an overhead as you understand).

You have simply to choose your way and follow it.

1
  • 4
    It's not just an opinion, is it? Hasn't software engineering research documented the relationship between live time and number of bugs since at least the 1980s? Commented Oct 9, 2011 at 18:00
1

Your two examples are functionally different code, they're not interchangeable. (Your stripped down examples leave it a distinction without a difference, but in non-trivial code it does make a difference). The rule you site is always subordinate to scoping considerations, as indicated by "...as possible".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.