Click here to Skip to main content
15,879,239 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I was just looking at a bit of code I had written and wondered why it didn't work.
Down to basics, it looked something like this...
VB
For i As Integer = 0 To 9
   Dim j As Integer
   j += 10
   If j = 10 Then
      Console.WriteLine("j is 10")
   End If
Next
How often do you think "j is 10" would be printed? I was sure it would be printed 10 times, once for each iteration. How wrong I was! It is only printed the first time because the second iteration it will be 20, then 30 and so on. This strikes me as very odd because j is declared within the For loop and is declared at every iteration, yet it behaves as if it was declared outside the iteration and the value is 'carried onto' the next iteration.

I don't think this behaviour can be caused in C# since you need to assign a value to a variable before you use it (so j will always have to be set at a default value before each iteration). You can check that j has a value before each iteration, but the first by setting a breakpoint, but it will never 'break' at runtime.

I've used many For loops, but never came across this one.
Could anyone explain why j does not go out of scope after an iteration? Or am I just talking crazy?
Thanks.
Posted

And to confirm André, this VB code:
VB
Sub Main()
    For i As Integer = 0 To 9
        Dim j As Integer
        j += 10
        If j = 10 Then
            Console.WriteLine("j is 10")
        End If
    Next

    Dim jp As Integer

    For i As Integer = 0 To 9
        jp += 10
        If jp = 10 Then
            Console.WriteLine("j is 10")
        End If
    Next

End Sub

Gives this decompiled VB via ILSpy:
VB
' ForNextTest.Module1
<stathread()>
Public Shared Sub Main()
	Dim i As Integer = 0
	' The following expression was wrapped in a checked-statement
	Dim arg_2B_0 As Integer
	Dim num As Integer
	Do
		Dim j As Integer
		j += 10
		Dim flag As Boolean = j = 10
		If flag Then
			Console.WriteLine("j is 10")
		End If
		i += 1
		arg_2B_0 = i
		num = 9
	Loop While arg_2B_0 <= num
	Dim k As Integer = 0
	Dim arg_57_0 As Integer
	Do
		Dim jp As Integer
		jp += 10
		Dim flag As Boolean = jp = 10
		If flag Then
			Console.WriteLine("j is 10")
		End If
		k += 1
		arg_57_0 = k
		num = 9
	Loop While arg_57_0 <= num
End Sub

I.e. both Do...Loop While blocks are identical.

Interestingly, the compiler decides that since the second For...Next loop uses the same identifier (i), it will use different ones, presumably since a For...Loop specifies a starting value (0 in this case), equivalent to i = 0, and it does that in preference to stuffing an i = 0 line (that you didn't write) in there.

So, if you don't specify a new value for a variable, which a For...Loop does, then it will retain the prior value if you use it again - which is what André (MSDN) says, elephanting annoying folk they are, always elephanting right(ish).

The IL Code is:
.method public static 
	void Main () cil managed 
{
	.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (
		01 00 00 00
	)
	// Method begins at RVA 0x2108
	// Code size 91 (0x5b)
	.maxstack 2
	.entrypoint
	.locals init (
		[0] int32 jp,
		[1] int32 i,
		[2] int32 j,
		[3] int32 i,
		[4] bool VB$CG$t_bool$S0,
		[5] int32 VB$CG$t_i4$S0
	)

	IL_0000: nop
	IL_0001: ldc.i4.0
	IL_0002: stloc.1
	// loop start (head: IL_0003)
		IL_0003: ldloc.2
		IL_0004: ldc.i4.s 10
		IL_0006: add.ovf
		IL_0007: stloc.2
		IL_0008: ldloc.2
		IL_0009: ldc.i4.s 10
		IL_000b: ceq
		IL_000d: stloc.s VB$CG$t_bool$S0
		IL_000f: ldloc.s VB$CG$t_bool$S0
		IL_0011: brfalse.s IL_001e

		IL_0013: ldstr "j is 10"
		IL_0018: call void [mscorlib]System.Console::WriteLine(string)
		IL_001d: nop

		IL_001e: nop
		IL_001f: nop
		IL_0020: ldloc.1
		IL_0021: ldc.i4.1
		IL_0022: add.ovf
		IL_0023: stloc.1
		IL_0024: ldloc.1
		IL_0025: ldc.i4.s 9
		IL_0027: stloc.s VB$CG$t_i4$S0
		IL_0029: ldloc.s VB$CG$t_i4$S0
		IL_002b: ble.s IL_0003
	// end loop
	IL_002d: ldc.i4.0
	IL_002e: stloc.3
	// loop start (head: IL_002f)
		IL_002f: ldloc.0
		IL_0030: ldc.i4.s 10
		IL_0032: add.ovf
		IL_0033: stloc.0
		IL_0034: ldloc.0
		IL_0035: ldc.i4.s 10
		IL_0037: ceq
		IL_0039: stloc.s VB$CG$t_bool$S0
		IL_003b: ldloc.s VB$CG$t_bool$S0
		IL_003d: brfalse.s IL_004a

		IL_003f: ldstr "j is 10"
		IL_0044: call void [mscorlib]System.Console::WriteLine(string)
		IL_0049: nop

		IL_004a: nop
		IL_004b: nop
		IL_004c: ldloc.3
		IL_004d: ldc.i4.1
		IL_004e: add.ovf
		IL_004f: stloc.3
		IL_0050: ldloc.3
		IL_0051: ldc.i4.s 9
		IL_0053: stloc.s VB$CG$t_i4$S0
		IL_0055: ldloc.s VB$CG$t_i4$S0
		IL_0057: ble.s IL_002f
	// end loop
	IL_0059: nop
	IL_005a: ret
} // end of method Module1::Main


M
 
Share this answer
 
v2
Comments
Sander Rossel 24-Nov-12 5:57am    
Thanks for the answer. Going all the way to IL surely proves the point ;)
I'm still surprised that it doesn't matter wether I declare j in or outside a For loop though. At least in VB (the same goes for C#, but unlike VB you're forced to assign a starting value to a variable before doing anything with it, eliminating the chances of still having a previous value).
M-Badger 24-Nov-12 6:12am    
You could argue that C# is hiding the behaviour that MSDN describes and that VB reveals in indirectly by doing 'weird' stuff. A win for VB.
Or you could say that C# is saying 'don't worry about what crazy stuff the compiler is going to do, I'll make you be sufficiently clear that it doesn't matter'.

A Win for C#. In some weird way the behaviour MS have chosen makes sense to me (maybe it goes back to having learnt procedural coding on a BBC Model B). Choice your posion and off you go, whatever you decide someone is gonna say 'brilliant' and right after someone else will say 'idiot'.
Sander Rossel 24-Nov-12 12:02pm    
I'd say it's not a win or loss for either language. You just have to know how it works :)
I found this on MSDN:
Even if the scope of a variable is limited to a block, its lifetime is still that of the entire procedure. If you enter the block more than once during the procedure, each block variable retains its previous value. To avoid unexpected results in such a case, it is wise to initialize block variables at the beginning of the block.
So the j variable is only given it's default value 0 the first iteration and after that retains the value it has at the end of the block.
 
Share this answer
 
Comments
Sander Rossel 24-Nov-12 5:52am    
Guess I missed that part... But it is exactly what I'm experiencing.
I am no expert but if I wrote that then I would expect what you describe, unless I had witten
VB
Dim j as integer = 0

Though I do sympathise since I would not know definitively what such code *should* do.

Indeed I woud probably write Dim j as integer outside the loop for fear (lack of knowledge) of redeclaring j repeatedly.

I would suspect that the compiler is intervening and reserving the memory location for j before the outer loop kicks in, hence j += 10 is adding 10 to whatever j was previously and not to the default value of an integer (zero).

If I had ILSpy or similar (and knew how to use it) then it should be easy to work out (but I don't, for either).
Maybe another reply can confirm (or rebut) via posting the IL Code (or maybe you can).

M
 
Share this answer
 
I can only say that happens in Visual Basic, as in c # does not know its exact behavior.
Although j is declared at the block level, its scope is procedural, so if you want to behave like block-level declared, j should be initialized in each iteration.
It is a usual behavior in VB.net and happens in any block structure.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900