OK, I’ve got a question for y’all. Take a look at the code sample at the end of this entry and leave me a comment on what you think the output of the program should be. Note that I’m looking for people’s opinions here and this isn’t one of those “how well do you know VB” trick questions. Once you’ve put in a comment on what you think the output should be, then feel free to run it and see what it does, but do hold off commenting on the actual behavior until people have had a chance to comment. Once we’ve got some comments, I’ll write a longer entry on why I’m asking this and why the answer matters at this particular moment in time.
Module Module1
Sub Main()
For i As Integer = 0 To 2
Dim x As Integer
Console.WriteLine(x)
x += 1
Next
Console.ReadLine()
End Sub
End Module
It should be
0
0
0
If you want
0
1
2
then x should be declared using the Static keyword instead of Dim.
I expect it to print
0
1
2
That’s because the Dim statement has no initialization indication and therefore the variable will be initialized to its default value when stack space is allocated to it. It seems to me that Dim is an execution statement only when some sort of initialization is explicitly indicated. Therefore, the Dim in the snippet is just a variable declaration (which implicitly clears the area that the variable will occupy, and thus, "initializes" it).
I can’t aggree with others that say that the compiler should warn about a non-initalized variable. VB’s tradition has allways been to auto-initialize variables with their default value upon declaration, it’s part of its semantic. If other languages require you to manually assign a value to a fresh variable, so be it, but that’s not how VB works. I frawn upon code that says something like "Dim B as Boolean = False". It seems to me as an overstatement.
Nevertheless, because there’s no explicit inicialization, the variable begins its life with the default value and keeps its last assigned value at each cycle, which is just as it should be, IMHO.
3 years ago, when I first approached VB.NET, I would expect it to be 0,0,0. Now I know that the result is 0,1,2.
I stumbled over this exact problem in production code countless times already, until I learned the behaviour to ALWAYS initialise the value inline, such as Dim a As Integer = 0.
What makes things worse: When you test the code, it often happens that you test the loop only ONCE (because of time constaints) – and the first interation is ok. You only notice it later, in production environment, that actually, the second and third interations don’t work anymore π
I would be very glad if the compiler would warn me in this case.
0
0
0
in my head
It’s gonna print 012, which irritates me. I don’t see any real value in VB Block scope variables. Their lifetime is the same as other variables local to the procedure, but their scope is just within the block. I think their life should be block scope as well.
At least we can work around this by initializing the variable on every loop.
Just reading that through in my head I would say
0
0
0
because the scope of X is limited to only the current itteration of the loop so the next time round X is a newly declared integer variable with the default value 0.
As an asside it that reminds me of the sort of abstract questions you might get in an interview.
"why I’m asking this and why the answer matters at this particular moment in time."
Hmm… because VB will support closures? π
I think it will output
0
1
2
I sorta think it should output
0
0
0
I would be critical of the person who wrote the code….
π
Bill McCarthy: Had to test it – VS2005 allows you to put on a breakpoint on a Dim statement (without initializers), but when the code is run the breakpoint is moved to the next (executable) statement.
Regarding the following:
******
page 180 of the VB langauge specification says:
"Variable initializers on local declaration statements are equivalent to assignment statements placed at the textual location of the declaration. Thus, if execution branches over the local declaration statement, the variable initializer is not executed. If the local declaration statement is executed more than once, the variable initializer is executed an equal number of times. "
this contradicts the behaviour.
*****
the first line says "Variable initializers…". There are no variable initializer in the given code…
Two paragraphs above the specification says:
"local variables are initialized to their type’s default value upon each entry into the method"
which is exactly what is happening.
I personally prefer the current behaviour, but that might be because I’m used to how vb does it. Though a warning might be a good idea.
Rolf: The document probably does mean explicit variable initializers, but it does not distinguish between explict and implicit initializers.
I think the current behaviour is undesirable, as the different opinions here lay testimony to. Ideally the language should be clearer on issues like this. Having the IDE or compiler warn on such constructs is really a second best option as that can just lead to excessive noise on a compile, but given the current situation the IDE interacting on such constructs is probably the best we can now hope for.
I do understand why the compiler does this for a number of reasons including backward compatibility, and the difficulty//almost impossiblity of implementing the variable as a true new allocation on each iteration. At best, it’d probably only be practical to implement it as the one slot that gets re-allocated. Given that constraint, the current implementation does offer some extra functionality, albeit via smoke and mirrors. In doing so I think it confuses some, and does not conform to the block scopign rules, rather the variable is scoped with the outer block (althoguh that is unlikely to bother people as to object lifetime except in some extreme cases)
So anyway, yes a rule such as "Warn on no explicit variable initialisers inside loop constructs" would possibly be helpful, as well as a <nowarn> attribute that cold be applied. If people do use this syntax, I want it obvious in their code they deliberately are doing so. Honestly, I don’t think there is any real scoping benifit, and in fact it is more prone to errors if you refactor etc. so I owuld be jsut as happy to see the IDE not allow that code, requiring the variable to be declared before the For Next block. doing that would make the code very clear to everyone and we wouldn’t have different interpretations. I think that is the ultimate goal.
So perhaps the best path forward is a compiler warnign in the next version, or two, warnign that this feature will be made obsolete, and this actual feature be totally deprecated for the sake of code clarity and developer sanity π
I assumed it would have output 000. If I had wanted the 012 result, I would have Dimmed it immediately before the For line. For no particular reason other than "It just feels right", I see Dim statements as the birth of the variable, not a continuation.
it *should* be: 0 0 0
0
1
2
Although, seriously, why not just Console.writeline (i)?
:/
NaN
NaN
NaN
?
Come to think about it 0,0,0 is prolly better π
I disagree with the claims that the variable’s Lifetime Scope is different from its accessibility. What we’re arguing over here is the very *definition* of scope.
If you rewrite the code removing the for loop (unrolling) then you could get :
Begin Scope of unrolled For loop
Dim x As Integer
‘ x = 0 ‘ If the var has explicit initializer
Console.WriteLine(x)
x += 1
‘ x = 0
Console.WriteLine(x)
x += 1
‘ x = 0
Console.WriteLine(x)
x += 1
End Scope
There’s nothing *wrong* with that per se. It’s just weird to anyone used to the behavior of C++, C#, etc, which is:
Begin Scope of unrolled For loop
Begin Scope of For loop contents
Dim x As Integer
Console.WriteLine(x)
x += 1
End Scope
Begin Scope of For loop contents
Dim x As Integer
Console.WriteLine(x)
x += 1
End Scope
Begin Scope of For loop contents
Dim x As Integer
Console.WriteLine(x)
x += 1
End Scope
End Scope
(Note, I’m not saying that loops are always unrolled by the compiler. I’m just trying to clarify the behavior.)
However, note that this problematic behavior does not exist for objects or valuetypes. Interestingly this means that the seemingly built-in type Date behaves differently.
For X As Integer = 0 To 2
Dim d As Date
Console.WriteLine(d.ToString)
d.Add(TimeSpan.FromTicks(1))
Next
Prints the same value three times, as do any user defined types.
So it seems you have a bug somewhere, because the behavior should at least be consistent. From an outsider’s perspective it would seem to be easiest to treat loops the same as in other commonly used languages, but I wouldn’t be too upset if you instead went the other way. Just make it consistent for all types.
If you *are* going to keep the current behavior then please, just for me, add an option to enable a warning whenever variables within loops are not explicitly initialized.
Btw, this is not my only problem with VB For loops. http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?FeedbackId=590bc265-84ac-45a2-a04a-b0b855ebd6ec
0
0
0
It should be 0, 1, 2 because when you define the variable you are then setting the for loop to run until the result reads 2. After the result is two the for loop closes because the result no longer reads the criteria set forth before the for loop.
Well i for one cant wait to see what Paul has to say, about the behaviour (especially if its a recomened practice or not, im starting to think not since C# doesnt do the same, and converting from VB to C# could lead to problems).
I noticed that looking at the IL of the decompiled method. The declarations for both variables (i and x) are made upon entry to the method and hence any more granular scope than the method level seems to only be an enforced rule for clarity rather than technical reasons.
That i did not expect (i expected the variables to be declared upon entry to the scope that contains it). So i was right but the reason was slightly off.
0 0 0
VB – Quit trying to hand hold all the people who don’t want to learn how to program correctly!
It’s got to be
0
0
0
Variable x gets declared (and initialized) within the loop everytime. It’s scope is within the loop.
Manish Jadhav
JustinM: Re the datetime code. The Add method does not change the intital variable, you’d have to assign it to the variable. So the behaviour you are seeign has nothign to do with loop scope. also, it is unlikely that adding 1 tick would change the date string in general format.
As ot the comment about objects, no, the behaviour is actually the same for objects, valuetypes and intrinsic types in regardss to scope and loop constructs. Tyr this simple code as an example:
For i As Int32 = 0 To 10
Dim obj As Object
If obj Is Nothing Then
Console.WriteLine(i)
obj = New Object
End If
Next
Console.ReadLine()
By now we all know the output is
0
1
2
I thought I understood why. However, add an additional Console.WriteLine(x) after the increment and the output is much different than I would have guessed.
The two behaviours seem to contradict:
Module Module1
Sub Main()
For i As Integer = 0 To 2
Dim x As Integer = 0
Console.WriteLine(x)
x += 1
Console.WriteLine(x)
Next
Console.ReadLine()
End Sub
End Module
Robert: I wodulk expect your output to be
0
1
0
1
0
1
Because you put an initializer on the declatation of x, so it would set x to 0 on each loop.
Grrr.
Am I the only one who finds it surprising that System.DateTime is immutable. I always forget this when I haven’t written .NET code for a while.
So, I guess we do have consistency. I just had a loose nut on my keyboard. π
It’s also interesting to note that you can get the behavior expected from other languages by always using New with value types. (Like C#)
For i As Integer = 0 To 2
Dim x As New Integer
Console.WriteLine(x)
x += 1
Next
Looks a little weird with primitive types though.
Justin: That would be expected behaviour as
Dim x as New Integer is shorthand for
Dim x as integer = new integer which is an initializer, which we know alters the behaviour.
There are some nifty constructors on some primitives though, integer doesnt have any but string does.
A poll with some radiobuttons might have been better, I’d really like to see a running tally.
For the time being I think it should output what it currently does.
If the code didn’t output 0, 1, 2 – if x were reinitialized each time then it would completely nullify the whole block level scoping. A For loop isn’t repeatedly leaving its own scope and reentering it. All the iterations are in a continuous scope.
The variable i is also within the scope of the For loop, it doesn’t reinitialize to 0 every iteration.
Block level scoping is good for reusing the For loop’s imfamous i, j, x, and y variables multipletimes in a procedure and it does save you variable initialization if you have lots of variables and complicated control structures (if ginormous procedures are the kinda thing you’re into).
As for matching up with C# behavior, I think we’ve burned that bridge a while ago. I resent the implication that C# sets the gold standard for what is and isn’t good practice. While I like them matching each other in capability I also like room for innovation and personality especially where and alternative is provided – that is to say, C# for loops don’t cache their upperbound – can you cache it? yes. Can you make it so that x reinitializes every iteration? yes.
And as for new programmers being confused? It’s called reading. If they can’t read a paragraph about block level scoping then they really aren’t at the level to be formulating assumptions about programming language behavior.
Lastly (and this may just be my browser) for the love of Jesus, Mary, Joseph, and all the Saints find a way to make this feedback box wider, PLEASE.
Tim, I realize that. I was just trying to point out that vb and c# could be considered consistent with respect to this feature, except that c# doesn’t allow declaring valuetypes without initializers.
I’m glad Paul brought this up, because I didn’t know it.
Justin: Sorry i didnt even see the (Like C#) bit there, i guess as usual my eyes jumped to the source code before finishing reading the preceeding text π
Only
0
0
0
will do as it is all about Scope and the scope of this declaration is "inside" the loop.
I like variables to have a potentially confined scope within routines – the alternative is to consider the Dim statement was at the top of the sub/function but that was no where the programmer put it.
I think it should error out with a runtime error for declaring an already declared variable (in the time honored tradition of needing a redim statement)
Of course, that’s not what it DOES do but…
Although Java on an MS VB thread will probably be about as popular as a New York strip steak at a vegetarian buffet, I’d like to share a perspective from another programming language.
0, 0, 0
is what makes sense to me, and here’s why. It does seem to me that the for loop’s scope is being entered afresh each time around the loop. Consider the following Java program:
public class Sevens {
public static void main(String[] args) {
for (String s : args) {
final int x = Integer.parseInt(s) * 7;
System.out.println(s + ": " + x);
}
}
}
We iterate over the command line parameters, converting each one to an int and multiplying by seven. Notice that the variable x’s scope is confined to the loop, and that it is declared "final", meaning that it cannot be changed once it has been set.
At first blush you might think this is not a legal Java program, but in fact it is, and does just what you would expect. Of course, one big difference may be that it is the BLOCK of the for loop (the curly braces) that introduces a new scope. If the understanding is that in VB it is the FOR keyword itself introducing the scope, I can see why one would argue that the scope is not being exited and entered each time around the loop.
I hope this outsider’s perspective is of some benefit. Consider it grist for the mill.
Well i’m a noob in the vb.net world yet… but, the logical answer according the documentation is 000… the local scope of var force the initialitation…
>>Do I want the compiler to be "smart" and know that it only has to create and initialize the variable once? Do I want the compiler to be literal and initilize the variable each time through the loop? Or do I want the compiler to be brain dead and give me an error?
I would prefer the compiler be brain dead. If the code is ambiguous, then warn me and don’t compile. That way I have to write what I want it do do… clearly and concisely.
Oh, and concerning a breakpoint on a declaration. You can’t put a breakpoint on:
Dim x As Integer
But you can put a breakpoint on:
Dim x As Integer = 0
it will give
0
1
2
Here’s a further thought – if you try:
For i As Integer = 0 To x
Dim x As Integer
Console.WriteLine(x)
x += 1
Next i
the first line doesn’t compile – x isn’t in scope.
Hmm – x is in the scope of the for loop, and so is i, but they’re not in the same scope… we have scopes like this:
Module
Sub
For + Next
For loop contents
Also, to show how much of a special case the for loop is, try the functionally equivalent:
Do While i As Integer <= 2
Dim x As Integer
Console.WriteLine(x)
x += 1
i += 1
Loop
Hey Paul, you didn’t forget about us now did you?
I’m a VB.NET guy and I agree with those who believe it should output:
0
0
0
Since it is declared within the body of the For…Next statement, the scope of x should be within the body of the loop and therefore initialized for each pass through the loop. x should not be in scope when the For…Next statement counter is evaluated.
Pingback: Panopticon Central
Pingback: Panopticon Central