Variable scope confusion in C#

There are two relevant rules here.

The first relevant rule is:

It is an error for a local variable
declaration space and a nested local
variable declaration space to contain
elements with the same name.

(And another answer on this page calls out another location in the specification where we call this out again.)

That alone is enough to make this illegal, but in fact a second rule makes this illegal.

The second relevant rule in C# is:

For each occurrence of a given
identifier as a simple-name in an
expression or declarator, within the
local variable declaration space,
immediately enclosing block, or
switch-block of that occurrence,
every other occurrence of the same
identifier as a simple-name in an
expression or declarator within the
immediately enclosing block or
switch-block must refer to the same
entity. This rule ensures that the
meaning of a name is always the same
within a given block, switch block,
for-, foreach- or using-statement, or
anonymous function.

(UPDATE: This answer was written in 2009; in recent versions of C# this rule has been eliminated because it was considered to be too confusing; the user confusion produced was not worth the small number of bugs that were prevented. See this answer for details.)

You also need to know that a for-loop is treated as though there are “invisible braces” around the whole thing.

Now that we know that, let’s annotate your code:

public void MyMethod()
{ // 1
    int i=10; // i1
    { // 2 -- invisible brace
      for(int x=10; x<10; x++) // x2
      { // 3
        int i=10;  // i3
        var objX = new MyOtherClass(); // objX3
      } // 3
    } // 2
    var objX = new OtherClasOfMine(); // objX1
} // 1

You have three “simple names”, i, x and objX. You have five variables, which I’ve labeled i1, x2, i3, objX3, and objX1.

The outermost block that contains usages of i and objX is block 1. Therefore, within block 1, i and objX must always refer to the same thing. But they do not. Sometimes i refers to i1 and sometimes it refers to i3. Same with objX.

x, however, only ever means x2, in every block.

Also, both “i” variables are in the same local variable declaration space, as are both “objX” variables.

Therefore, this program is an error in several ways.

In your second program:

public void MyMethod()
{ // 1
    { // 2 -- invisible 
      for(int x=10; x<10; x++)   // x2
      { // 3
        int i=10;  // i3
        var objX = new MyOtherClass(); // objX3
      } //3 
    } // 2
    { // 4 -- invisible
      for(int x=10; x<10; x++)  // x4
      { // 5
        int i=10;  // i5
        var objX = new MyOtherClass();  // objX5
      } //5
   } // 4
} // 1

Now you have three simple names again, and six variables.

The outermost blocks that first contain a usage of simple name x are blocks 2 and 4. Throughout block 2, x refers to x2. Throughout block 4, x refers to x4. Therefore, this is legal. Same with i and objX — they are used in blocks 3 and 5 and mean different things in each. But nowhere is the same simple name used to mean two different things throughout the same block.

Now, you might note that considering all of block 1, x is used to mean both x2 and x4. But there’s no mention of x that is inside block 1 but NOT also inside another block. Therefore we don’t count the inconsistent usage in block 1 as relevant.

Also, none of the declaration spaces overlap in illegal ways.

Therefore, this is legal.

Leave a Comment