C#:
static class Module1
{
public static void Main()
{
for (index = 1; index <= GetCount(); index++) {
Console.WriteLine("For {0}", index);
}
Console.ReadKey();
}
public static int GetCount()
{
Console.WriteLine("GetCount");
return 10;
}
}
Result (C# rechecks the condition):
GetCount
For 1
GetCount
For 2
GetCount
For 3
GetCount
For 4
GetCount
For 5
GetCount
For 6
GetCount
For 7
GetCount
For 8
GetCount
For 9
GetCount
For 10
GetCount
VB.NET
Module Module1
Sub Main()
For index = 1 To GetCount()
Console.WriteLine("For {0}", index)
Next
Console.ReadKey()
End Sub
Public Function GetCount() As Integer
Console.WriteLine("GetCount")
Return 10
End Function
End Module
Result (VB.NET does not recheck the condition):
GetCount
For 1
For 2
For 3
For 4
For 5
For 6
For 7
For 8
For 9
For 10
a) Why doesn't VB.NET respect the "rule" of the recheck the For condition on each iteration?
b) Is there a way to force VB to re-check this condition?
C# and VB.NET are different languages, and similar keywords can have different semantics.
From the docs for For ... Next
(my emphasis) :
When a
For...Next
loop starts, Visual Basic evaluatesstart
,end
, andstep
. This is the only time it evaluates these values.
From section 8.8.3 of the C# specification (ditto):
When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the expressions of the for-iterator, if any, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the for-condition in the step above.
If you do want to force the condition to be checked every time, VB.NET offers the extremely flexible Do ... Loop which can have a While or an Until condition, operating at the start or the end of the loop. With that statement, the loop condition is evaluated every time round.
You can achieve the same effect in VB.NET using while(condition)
.
This is the difference with the compiler. VB.NET compiler just behaves differently.
if you use reflector on the VB.NET one, you see this C# reflected code:
[STAThread]
public static void Main()
{
int VB$t_i4$L0 = GetCount();
for (int index = 1; index <= VB$t_i4$L0; index++)
{
Console.WriteLine("For {0}", index);
}
Console.ReadKey();
}
And here is the IL code (note IL_002):
.method public static void Main() cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// Code size 47 (0x2f)
.maxstack 2
.locals init ([0] int32 index,
[1] int32 VB$t_i4$L0,
[2] int32 VB$CG$t_i4$S0)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: call int32 ConsoleApplication2.Module1::GetCount()
IL_0007: stloc.1
IL_0008: stloc.0
IL_0009: br.s IL_0021
IL_000b: ldstr "For {0}"
IL_0010: ldloc.0
IL_0011: box [mscorlib]System.Int32
IL_0016: call void [mscorlib]System.Console::WriteLine(string,
object)
IL_001b: nop
IL_001c: nop
IL_001d: ldloc.0
IL_001e: ldc.i4.1
IL_001f: add.ovf
IL_0020: stloc.0
IL_0021: ldloc.0
IL_0022: ldloc.1
IL_0023: stloc.2
IL_0024: ldloc.2
IL_0025: ble.s IL_000b
IL_0027: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_002c: pop
IL_002d: nop
IL_002e: ret
} // end of method Module1::Main
While for the C# code it is different (check is inside the loop):
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 50 (0x32)
.maxstack 2
.locals init ([0] int32 index,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_001c
IL_0005: nop
IL_0006: ldstr "For {0}"
IL_000b: ldloc.0
IL_000c: box [mscorlib]System.Int32
IL_0011: call void [mscorlib]System.Console::WriteLine(string,
object)
IL_0016: nop
IL_0017: nop
IL_0018: ldloc.0
IL_0019: ldc.i4.1
IL_001a: add
IL_001b: stloc.0
IL_001c: ldloc.0
IL_001d: call int32 Module1::GetCount()
IL_0022: cgt
IL_0024: ldc.i4.0
IL_0025: ceq
IL_0027: stloc.1
IL_0028: ldloc.1
IL_0029: brtrue.s IL_0005
IL_002b: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_0030: pop
IL_0031: ret
} // end of method Module1::Main
The VB.NET for-loop doesn't work the same way as the C# one. It says go from this value, to that value. The 'that value' is evaluated once.
The C# one is basically for('initialise stuff';'conditional break stuff';'incremental stuff')
, it evaluates the 'conditional break stuff' each time.
With a simple for-loop in C# it looks the same as the VB one, but it is (as you've found) working differently.
The reason is that the VB For
can be translated into something like this in C#:
int count = GetCount();
for (index = 1; index <= count; index++)
{
}
or, using linq to resemble VB:
foreach(int i in Enumerable.Range(1,GetCount())
{
}
In both cases (and in VB version) GetCount()
is called once, hence one call to Console.WriteLine("GetCount")
only.
Well, the short answer is they're different languages and they have slightly different takes on this keyword.
In the C#, iteration continues until the termination condition evaluates to false, and it's evaluated on each iteration.
In VB.NET, we iterate one time for each integer value from Start to End (provided the step keyword is not present) and then stop. End is evaluated once at the beginning and that's it.
You could get closer to the C# type of behavior in VB.NET using a Do loop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With