Variables retain their value in a loop?

Viewed 395

If I run this simple code in a console app:

For i As Integer = 1 To 10
    Dim s As String = i.ToString()
    Dim x As Decimal
    If i = 1 Then
        x = Decimal.Parse(s)
    End If
    Console.WriteLine(x.ToString())
Next
Console.ReadLine()

Unexpectedly, x retains its value of 1 and so 1 gets printed 10 times. I thought each iteration of the loop was its own code block, and that the state didn't carry over? Why does this happen? I would expect x to have the default value of System.Decimal.

Same thing happens in C#, except that the complier won't let you call ToString() on an uninitialized variable, but if you set a breakpoint in Visual Studio, you can see that x retains its value of 1.

for (int i = 1; i <= 10; i++)
{
    string s = i.ToString();
    Decimal x;
    if(i == 1)
    {
        x = Decimal.Parse(s);
    }
    // Value of x remains 1
}
Console.ReadLine();
3 Answers

Regarding VB.NET take a look about scope here. In the "Block Scope" section there is a note which states the following:

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 this behavior is by design and you should initialize the variable to whatever value your code needs.

I modified your code to show that the first time x is initialized to 0 but after that it retains the value of 1.

    For i As Integer = 1 To 10
        Dim s As String = i.ToString()
        Dim x As Decimal
        Console.WriteLine(x.ToString())
        If i = 1 Then
            x = Decimal.Parse(s)
        End If

        Console.WriteLine(x.ToString())
    Next

    Console.ReadLine()

thought each iteration of the loop was its own code block, and that the state didn't carry over

It does "carry over."

int i is scoped to the entire range of the loop, with values from 1 to 10.

The if statement only executes on the first iteration, and x is scoped as a local variable to the outer method (assuming the loop is entered), not only the loop body. That being said, after & outside the loop, x == 1 as well.

In fact, Decimal x; in the loop is redundant, and you have the same execution logic as

string s;
Decimal x;
for (int i = 1; i <= 10; i++)
{
    s = i.ToString();

    if(i == 1)
    {
        x = Decimal.Parse(s);
    }
    Console.WriteLine(x);
}
// x still defined here

To make some sense of what you want, it should be just like this:

For i As Integer = 1 To 10
    If i = 1 Then
      Console.WriteLine(Decimal.Parse(i.ToString()))
    End If
Next

This will give you the same result you want but it is readable.

Related