views:

246

answers:

9
for (int i = 0; i < 10; i++)
{
    Foo();
}
int i = 10; // error, 'i' already exists

----------------------------------------    

for (int i = 0; i < 10; i++)
{
    Foo();
}
i = 10; // error, 'i' doesn't exist

By my understanding of scope, the first example should be fine. The fact neither of them are allowed seems even more odd. Surely 'i' is either in scope or not.

Is there something non-obvious about scope I don't understand which means the compiler genuinely can't resolve this? Or is just a case of nanny-state compilerism?

A: 

Or is just a case of nanny-state compilerism?

Exactly that. There is no sense in "reusing" variable names in the same method. It's just a source of errors and nothing more.

Foxfire
The problem is not in reusing variable names; it is perfectly legal to reuse a variable name several times in one method. It is not legal to use the same *simple name* to refer to *two different things* within a particular local variable declaration space.
Eric Lippert
+1  A: 

In the first example, the declaration of i outside of the loop makes i a local variable of the function. As a result, it is an error to have another variable name i declared within any block of that function.

The second, i is in scope only during the loop. Outside of the loop, i can no longer be accessed.

So you have seen the errors, but there is nothing wrong with doing this

for (int i = 0; i < 10; i++)
{
  // do something
}

foreach (Foo foo in foos)
{
   int i = 42;
   // do something 
}

Because the scope of i is limited within each block.

Anthony Pegram
+8  A: 

It is because the declaration space defines i at the method level. The variable i is out of scope at the end of the loop, but you still can't redeclare i, because i was already defined in that method.

Scope vs Declaration Space:

http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx

You'll want to take a look at Eric Lippert's answer (who by default is always right concerning questions like these).

http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx

Here is a comment from eric on the above mentioned post that I think talks about why they did what they did:

Look at it this way. It should always be legal to move the declaration of a variable UP in the source code so long as you keep it in the same block, right? If we did it the way you suggest, then that would sometimes be legal and sometimes be illegal! But the thing we really want to avoid is what happens in C++ -- in C++, sometimes moving a variable declaration up actually changes the bindings of other simple names!

Kevin
But then why can you define `i` in scope of another loop in the same method? I still think it's just bureaucracy on the part of the compiler.
Michael Meadows
@Michael: You can do that because the loops are sibling scopes. There is no collision defining identically named variables in sibling scopes because they are out of scope of each other.
Zach Johnson
@Zach: However the first block in the original question shows exactly that: There is no collision. But it still won't compile.
Foxfire
wow, I had no idea this existed. I'm going to make sure to add this to my list of reasons I hate C#
rmeador
No reason to hate it for that. It's behaving exactly as it should be: preventing you from shooting yourself in the foot for reasons that are not even worth talking about (reusing variable names).
Foxfire
@Foxfire: The first example in the question is a parent-child scope relationship, causing a collision. Sibling scopes are scopes which are both children of the parent scope.
Zach Johnson
Really? I like it that way. I can't think of a reason to re declare a variable with the same name in the same method, and it prevents me (or someone else) from moving a variable around and causing an unexpected behavior in the future.
Kevin
Thanks for the shout-out. Couple things. First, this is a better article: http://blogs.msdn.com/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx and second, better to link to the original rather than the copy; that way you get my updates and get to read the comments of others: http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx
Eric Lippert
@Zach: Well this answer (and the linked documents) say otherwise: They are in different declaration spaces. However in terms of scope both could happily exist because they are both children (non-overlapping) of the method. The i-loop is out of scope at the end of the loop!
Foxfire
Obviously my comment above should read (just to correct that): They are in the same declaration space.
Foxfire
@Foxfire: I think the question (at least in its current form) is about the scope of local variables. The `i` in the `for` loop is in a child scope and the `i` written after the loop is in the parent scope of the loop, which is causing the collision. See my answer: http://stackoverflow.com/questions/2693138/variable-scope-in-statement-blocks/2693193#2693193
Zach Johnson
@meatador You find it a reason to hate a language because it enforces rules that prevent some really nasty, hard-to-spot bugs with virtually no inconvenience to the programmer?
Dan Diplo
A: 

Me thinks that the compiler means to say that i has been declared at the method level & scoped to within the for loop.

So, in case 1 - you get an error that the variable already exists, which it does

& in case 2 - since the variable is scoped only within the for loop, it cannot be accessed outside that loop

To avoid this, you could:

var i = 0;

for(i = 0, i < 10, i++){
}

i = 10;

but I can't think of a case where you would want to do this.

HTH

Sunny
A: 

you need to do

            int i ;
            for ( i = 0; i < 10; i++)
            {

            }
            i = 10;
anishmarokey
That is evil code.
Foxfire
A: 

Yea, I second the "nanny-state compilerism" comment. What's interesting is that this is ok.

for (int i = 0; i < 10; i++)
{

}

for (int i = 0; i < 10; i++)
{

}

and this is ok

for (int i = 0; i < 10; i++)
{

}

for (int j = 0; j < 10; j++)
{
    var i = 12;                
}

but this is not

for (int i = 0; i < 10; i++)
{
    var x = 2;
}

var x = 5;

even though you can do this

for (int i = 0; i < 10; i++)
{
    var k = 12;
}

for (int i = 0; i < 10; i++)
{
    var k = 13;
}

It's all a little inconsistent.

EDIT

Based on the comment exchange with Eric below, I thought it might be helpful to show how I try to handle loops. I try to compose loops into their own method whenever possible. I do this because it promotes readability.

BEFORE

/*
 * doing two different things with the same name is unclear
 */
for (var index = 0; index < people.Count; index++)
{
    people[index].Email = null;
}
var index = GetIndexForSomethingElse(); 

AFTER

/*
 * Now there is only one meaning for index in this scope
 */
ClearEmailAddressesFor(people); // the method name works like a comment now
var index = GetIndexForSomethingElse();

/*
 * Now index has a single meaning in the scope of this method.
 */
private void ClearEmailAddressesFor(IList<Person> people)
{
    for (var index = 0; index < people.Count; index++)
    {
        people[index].Email = null;
    }
}
Michael Meadows
It is not inconsistent. The rule is that the same simple name may not be used to denote two different things *throughout the outermost local variable declaration space in which the simple name is directly used*. A for loop defines a local variable declaration space. I think you'll find that this rule is consistently applied in each of your examples.
Eric Lippert
It may be consistent when you parse the language of the specification, but from a consumer's perspective it is inconsistent. If a variable is out of scope and can't be used, then there's no good reason that I shouldn't be able to create another variable of the same name.
Michael Meadows
There is a *very good reason*. The very good reason is because using the same simple name to refer to two different entities in overlapping declaration spaces is *confusing* and *bug prone* and therefore should be *illegal*.
Eric Lippert
@Eric I think your opinion on this is flavored by the fact that you're intimately familiar with what's under the hood. From the perspective of a simple consumer, if I have no ability to access or modify a variable, then it is effectively out of scope. In that case, regardless of how scope is handled _technically_, it should be treated by the language as out of scope. If I can't create a variable outside a loop that was previously declared inside that loop (see my third example), then it _is_ confusing, because the variable in the loop is out of context, if not technically out of scope.
Michael Meadows
It *is* treated as out of scope. As I've said several times, this isn't an issue of scope at all. This is an issue of *using the same sequence of letters to mean two different things in overlapping regions of code*. Stop thinking about scope; this isn't a scoping issue in the first place.
Eric Lippert
Ok, so I made a semantic misstep, and I apologize. As a developer, I would prefer to think of a for loop as a completely autonomous block of code. The problem can be solved by composing the loop into another method (the latter of which is preferred anyway since it usually promotes readability). Thanks for the informative comments.
Michael Meadows
+4  A: 

From the C# spec on local variable declarations:

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

Now, of course, you can't use i before it is declared, but the i declaration's scope is the entire block that contains it:

{
    // scope starts here
    for (int i = 0; i < 10; i++)
    {
        Foo();
    }
    int i = 10;
}

The for i variable is in a child scope, hence the collision of variable names.

If we rearrange the position of the declaration, the collision becomes clearer:

{
    int i = 10;

    // collision with i
    for (int i = 0; i < 10; i++)
    {
        Foo();
    }
}
Zach Johnson
thats a really clear illustration zach, thanks.
fearofawhackplanet
+11  A: 

By my understanding of scope, the first example should be fine.

Your understanding of scope is fine. This is not a scoping error. It is an inconsistent use of simple name error.

int i = 10; // error, 'i' already exists

That is not the error that is reported. The error that is reported is "a local variable named i cannot be declared in this scope because it would give a different meaning to i which is already used in a child scope to denote something else"

The error message is telling you what the error is; read the error message again. It nowhere says that there is a conflict between the declarations; it says that the error is because that changes the meaning of the simple name. The error is not the redeclaration; it is perfectly legal to have two things in two different scopes that have the same name, even if those scopes nest. What is not legal is to have one simple name mean two different things in nested local variable declarations spaces.

You would get the error "a local variable named i is already defined in this scope" if instead you did something like

int i = 10;
int i = 10;

Surely 'i' is either in scope or not.

Sure -- but so what? Whether a given i is in scope or not is irrelevant. For example:

class C 
{
    int i;
    void M()
    {
        string i;

Perfectly legal. The outer i is in scope throughout M. There is no problem at all with declaring a local i that shadows the outer scope. What would be a problem is if you said

class C 
{
    int i;
    void M()
    {
        int x = i;
        foreach(char i in ...

Because now you've used i to mean two different things in two nested local variable declaration spaces -- a loop variable and a field. That's confusing and error-prone, so we make it illegal.

Is there something non-obvious about scope I don't understand which means the compiler genuinely can't resolve this?

I don't understand the question. Obviously the compiler is able to completely analyze the program; if the compiler could not resolve the meaning of each usage of i then how could it report the error message? The compiler is completely able to determine that you've used 'i' to mean two different things in the same local variable declaration space, and reports the error accordingly.

Eric Lippert
A: 

interesting

class Test
{

 int i  ;
 static int si=9; 

public Test()
{
     i = 199;
}

static void main() {

for (int i = 0; i < 10; i++)
{
         var x = 2;
}

{ var x = 3; }

{    // remove outer "{ }" will generate compile error
         int si = 3; int i = 0;

         Console.WriteLine(si);
         Console.WriteLine(Test.si);

         Console.WriteLine(i);
         Console.WriteLine((new Test()).i);
 }

}

}

Mister_Egg_Head