Variable scope confusion in C#

2019-01-02 20:48发布

I have two code samples. The first does not compile, but the second does.

Code Sample 1 (does not compile)

public void MyMethod(){
    int i=10;

    for(int x=10; x<10; x++) {
        int i=10; // Point1: compiler reports error
        var objX = new MyOtherClass();
    }

    var objX = new OtherClassOfMine(); // Point2: compiler reports error
}

I understand why the compiler reports an error at Point1. But I don't understand why it reports an error at Point2. And if you say it is because of the organization inside MSIL, then why does the second code example compile?

Code sample 2 (compiles)

public void MyMethod(){

    for(int x=10; x<10; x++) {
        int i=10; 
        var objX = new MyOtherClass();
    }

    for(int x=10; x<10; x++) {
        int i=10; 
        var objX = new MyOtherClass();
    }
}

If the simple rules of variable scope apply in Code Sample 2, then why don't those same rules apply to Code Sample 1?

标签: c# scope
4条回答
只靠听说
2楼-- · 2019-01-02 21:26

You are allowed to use the same variable name in non-overlapping scopes. If one scope overlaps another, though, you cannot have the same variable declared in both. The reason for that is to prevent you from accidentally using an already-used variable name in an inner scope, like you did with i in the first example. It's not really to prevent the objX error since that would, admittedly, not be very confusing, but the error's a consequence of how the rule is applied. The compiler treats objX as having provenance throughout the scope in which it is declared both before and after its declaration, not just after.

In the second example the two for loops have independent, non-overlapping scopes, so you are free to re-use i and objX in the second loop. It's also the reason you can re-use x as your loop counter. Obviously, it would be a dumb restriction if you had to make up different names for each for(i=1;i<10;++i) style loop in a function.

On a personal note, I find this error annoying and prefer the C/C++ way of allowing you do to whatever you want, confusion be damned.

查看更多
临风纵饮
3楼-- · 2019-01-02 21:30

you should not be getting a compilation error with the second sample. Try renaming the variables to different letters/names and recompile again as it may be so other issue with the code most likely you've missed a curly bracket and changed the variables scope range.

查看更多
宁负流年不负卿
4楼-- · 2019-01-02 21:38

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.

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.

查看更多
墨雨无痕
5楼-- · 2019-01-02 21:40

From the C# Language Specification...

The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the local-variable-declarator of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name.

In code sample 1, both i and objX are declared in the scope of the function, so no other variable in any block inside that function can share a name with them. In code sample 2, both objXs are declared inside of the for loops, meaning that they do not violate the rule of not redeclaring local variables in inner scopes from another declaration.

查看更多
登录 后发表回答