134

I have been using C# for quite a long time but never realised the following:

 public static void Main()
 {
     for (int i = 0; i < 5; i++)
     {

     }

     int i = 4;  //cannot declare as 'i' is declared in child scope                
     int A = i;  //cannot assign as 'i' does not exist in this context
 }

So why can I not use the value of 'i' outside of the for block if it does not allow me to declare a variable with this name?

I thought that the iterator variable used by a for-loop is valid only in its scope.

Jeff B
  • 8,572
  • 17
  • 61
  • 140
John V
  • 4,855
  • 15
  • 39
  • 63
  • 8
    Because the outer block scope includes the scope of the for loop – V4Vendetta Nov 03 '11 at 09:08
  • 3
    One way I think of it (and some code style guides require this, especially for dynamic-typed languages) is that all variables declared in a scope could be declared at the start of that scope, meaning that your function could be re-written `int i, A; for(int i = 0; i < 5; i++){ } i=4; A=i` – Keith Nov 03 '11 at 09:31
  • 2
    @V4Vendetta: It's more the other way around. The inner block is a black box to the parent block. – Sebastian Mach Nov 03 '11 at 10:30
  • 4
    Aside from the technical reasons why this isn't possible, why would this (or a variant of this) ever be a sensible thing to do?! It obviously serves a different purpose, so give it a different name. – Dave Nov 03 '11 at 11:11
  • I didn't realise this at all but it seems like a completly daft restriction. – alan2here Nov 03 '11 at 19:00
  • Nice, I didn't realize C# did this (it's definitely a good thing). Though, I wouldn't want to be the person who had to figure out all the corner-cases. – BlueRaja - Danny Pflughoeft Nov 03 '11 at 19:28
  • @Keith: I think you mean `int i, A; for(i = 0; i < 5; i++){ } i=4; A=i` – Mike Chamberlain Nov 09 '11 at 05:41
  • @Mikey Cee - not really, you've corrected my statement so that it will compile, while what I was trying to show was why that `int` declaration was causing the compilation to fail. The OP's compilation fails because his code should be able to be refactored to my example and still compile - when you do it my way round it's obvious that you have `int i` twice. – Keith Nov 09 '11 at 10:09

9 Answers9

120

The reason you are not allowed to define a variable with the same name in both the for-loop as well as outside the for-loop is because variables in the outer-scope are valid in the inner-scope. Meaning that there would be two 'i' variables within the for-loop if this was allowed.

See: MSDN Scopes

Specifically:

The scope of a local variable declared in a local-variable-declaration (Section 8.5.1) is the block in which the declaration occurs.

and

The scope of a local variable declared in a for-initializer of a for statement (Section 8.8.3) is the for-initializer, the for-condition, the for-iterator, and the contained statement of the for statement.

And also: Local variable declarations (Section 8.5.1 of the C# specification)

Specifically:

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.

(Emphasis mine.)

Which means that the scope of the i inside your for-loop, is the for-loop. Whereas the scope of the i outside of your for-loop is the entire main method plus the for-loop. Meaning you'd have two occurrences of i inside the loop which is invalid according to the above.

The reason why you're not allowed to do int A = i; is because int i is only scoped for use within the for loop. Thus it is no longer accessible outside of the for loop.

As you can see both of these issues are a result of scoping; the first issue (int i = 4;) would result in two i variables within the for loop scope. Whereas int A = i; would result in access to a variable that is out of scope.

What you could do instead is declare i to be scoped to the entire method, and then use it in both the method as well as the for-loop scope. This will avoid breaking either rule.

public static void Main()
{
    int i;

    for (i = 0; i < 5; i++)
    {

    }

    // 'i' is only declared in the method scope now, 
    // no longer in the child scope -> valid.
    i = 4;

    // 'i' is declared in the method's scope -> valid. 
    int A = i;
}

EDIT:

The C# compiler could of course be changed to allow this code to compile quite validly. After all this is valid:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

for (int i = 5; i > 0; i--)
{
    Console.WriteLine(i);
}

But would it really be beneficial to your code readability and maintainability to be able to write code such as:

public static void Main()
{
    int i = 4;

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
    }

    for (int i = 5; i > 0; i--)
    {
        Console.WriteLine(i);
    }

    Console.WriteLine(i);
}

Think about the potential for mistakes here, does the last i print out 0 or 4? Now this is a very small example, one which is quite easy to follow and track but it is definitely a lot less maintainable and readable than having declared the outer i by a different name.

N.B:

Please note, C#'s scoping rules differ from C++'s scoping rules. In C++ variables are only in scope from where they are declared until the end of the block. Which would make your code a valid construct in C++.

Jeff B
  • 8,572
  • 17
  • 61
  • 140
Johannes Kommer
  • 6,401
  • 1
  • 39
  • 45
  • Makes sense, i think it should error on the inner `i` variable though; that seems more obvious to me. – George Duckett Nov 03 '11 at 09:15
  • But the scope is for command and its {}? Or does it mean its parent {}? – John V Nov 03 '11 at 09:15
  • 2
    Well if I declare 'A' AFTER the for statement, it is not valid in the for loop as it is declared later. That is why I do not get why the same name cannot be used. – John V Nov 03 '11 at 09:20
  • Imagine trying to code the logic for the compiler that would allow for that. – ChrisBD Nov 03 '11 at 09:42
  • I don't think the IL adds any value here. The C# compiler could be changed to compile the OP's sample. This is a designed feature, not a necessity. – H H Nov 03 '11 at 10:14
  • 9
    Perhaps this answer should, for completeness, point out that the C# scope rules are __different__ from those for C++ in this case. In C++, variables are only in scope from where they are declared until the end of the block (see http://msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspx). – AAT Nov 03 '11 at 10:31
  • @AAT Great suggestion, I've updated my answer to include a reference to that page aswell, thanks! – Johannes Kommer Nov 03 '11 at 10:59
  • 2
    Note that Java takes an intermediate approach between C++ and C#: as it is the OP's example would be valid in Java, but if the outer `i` definition was moved before the for loop, the inner `i` definition would be marked as invalid. – Nicola Musatti Nov 03 '11 at 13:06
29

J.Kommer's answer is correct: briefly, it is illegal for a local variable to be declared in a local variable declaration space that overlaps another local variable declaration space that has a local of the same name.

There is an additional rule of C# that is violated here as well. The additional rule is that it is illegal for a simple name to be used to refer to two different entities inside two different overlapping local variable declaration spaces. So not only is your example illegal, this is illegal too:

class C
{
    int x;
    void M()
    {
        int y = x;
        if(whatever)
        {
            int x = 123;

Because now the simple name "x" has been used inside the local variable declaration space of "y" to mean two different things -- "this.x" and the local "x".

See http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/ for more analysis of these issues.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
13

There is a way of declaring and using i inside the method after the loop:

static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

    {
        int i = 4;
        int A = i;
    }
}

You can do this in Java (it might originate from C I'm not sure). It is of course a bit messy for the sake of a variable name.

Chris S
  • 64,770
  • 52
  • 221
  • 239
7

If you'd declared i before your for loop, do you think it should still be valid to declare it inside the loop?

No, because then the scope of the two would overlap.

As for not being able to do int A=i;, well that's simply because i only exists in the for loop, like it should do.

Widor
  • 13,003
  • 7
  • 42
  • 64
7

In addition to J.Kommer's answer (+1 btw). There's this in the standard for NET scope:

block If you declare a variable within a block construct such as an If statement, that variable's scope is only until the end of the block. The lifetime is until the procedure ends.

Procedure If you declare a variable within a procedure, but outside of any If statement, the scope is until the End Sub or End Function. The lifetime of the variable is until the procedures ends.

Thus the int i decalared within the for loop header will be in scope only during the for loop block, BUT it's lifetime lasts until the Main() code completes.

ChrisBD
  • 9,104
  • 3
  • 22
  • 35
5

The easiest way to think about this is to move the outer declaration of I to above the loop. It should become obvious then.

It's the same scope either way, therefore can't be done.

Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
4

Also C#'s rules are many time not necessary in terms of programing strictly, but are there to keep your code clean and readable.

for example, they could have made it so that if you define it after the loop then it is ok, however it someone who reads your code and missed the definition line may think it has to do with the loop's variable.

Guy
  • 5,370
  • 6
  • 25
  • 30
2

Kommer's answer is technically correct. Let me paraphrase it with a vivid blind-screen metaphor.

There is a one way blind screen between the for-block and the enclosing outer block such that the code from within the for-block can see the outer code but the code in the outer block cannot see the code inside.

Since the outer code cannot see inside , it cannot use anything declared inside. But since the code in the for-block can see both inside and outside , a variable declared at both places cannot be used unambiguously by name.

So either you don't see it , or you C# !

explorer
  • 11,710
  • 5
  • 32
  • 39
0

Look at it in the same way as if you could declare an int in a using block:

using (int i = 0) {
  // i is in scope here
}
// here, i is out of scope

However, since int does not implement IDisposable, this can not be done. It may help someone visualize how an int variable is placed in a private scope, though.

Another way would be to say,

if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

Hope this helps to visualize what is going on.

I really like this feature, as declaring the int from inside the for loop keeps the code nice and tight.

  • WTF? If you are going to tag a NEG, have the balls to say why. –  Nov 07 '11 at 14:15
  • Not the downvoter and I think the comparison with `using` is quite ok, though the semantics are rather different (which is perhaps why it was downvoted). Note that `if (true)` is redundant. Remove it and you have a scoping block. – Abel Nov 08 '11 at 19:22