Are C# scoping rules different from C++

  • Thread starter Thread starter Joel Gordon
  • Start date Start date
J

Joel Gordon

Hi,

When I try and compile the a class containing the following method :

public void doSomething() {
for (int i=0; i<5; i++) {
IList list = new ArrayList();
Console.WriteLine( i / (list.Count) );
}
int i = 23;
IList list = new ArrayList();
Console.WriteLine( i / (list.Count) );
}

I get the following errors :

D:\Forecaster\TestForecaster\TestBaseEntities.cs(402): 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

D:\Forecaster\TestForecaster\TestBaseEntities.cs(402): The name 'i' does not
exist in the class or namespace 'ForestResearch.UnitTests.TestBaseEntities'

D:\Forecaster\TestForecaster\TestBaseEntities.cs(403): A local variable
named 'list' cannot be declared in this scope because it would give a
different meaning to 'list', which is already used in a 'child' scope to
denote something else

D:\Forecaster\TestForecaster\TestBaseEntities.cs(403): The name 'list' does
not exist in the class or namespace
'ForestResearch.UnitTests.TestBaseEntities'

The C# Language spec states :
a.. The scope of a local variable declared in a for-initializer of a for
statement (§8.8.3) is the for-initializer, the for-condition, the
for-iterator, and the contained statement of the for statement.
If the put the statements following the for statement in an anonymous block
like this then the compiler is happy :

public void doSomething() {
for (int i=0; i<5; i++) {
IList list = new ArrayList();
Console.WriteLine( i / (list.Count) );
}
{
int i = 23;
IList list = new ArrayList();
Console.WriteLine( i / (list.Count) );
}
}

Interestingly if I do the converse (i.e. put the for statement in an
anonymous block and DON'T put the statements following in an anonymous
block) then I get the same compiler errors!

I'm using Microsoft Visual Studio 2002.

Questions :

1. I would have thought that the scope of 'i' is the for initializer and for
statement so that this identifier could be used again as I have done above
in my first example ?

2. Likewise I would have thought that the scope of the variable 'list'
should just be the block in which it is declared and so could be used again
as I have done in my first example ?

3. Why does my second example produce no compiler errors ?


Thanks in advance,
Joel Gordon.
 
C# scoping specifications try "intelligently" protecting developers from making mistakes of using a variable accidentally. Hence, the compiler is designed to produce an error when such a scoping is made.

The paranthesis prove that you are aware of the scoping and hence you're informing the compiler explicitly of your intentions.

However, this is a pretty lame feature of the compiler - maybe helps at times. But try doing something like this:

class CTest
{
public void foo()
{
int x = 4;
Console.WriteLine( x );
}

int x = -1;
}

The compiler will compile without a problem!
 
Apparently C# doesn't like variables with the same name in *nested* scopes.

kevin aubuchon
 
It's prohibited to declare the same variable name more than once within a
code block or any children of that block.

I have to agree, I find *your* understanding of how it should work more
intuitive, but that's the way it is, for better or for worse.

Your second example works because "i" for example exists in two different
code blocks that are not in a parent/child relationship -- they are what I
would term "peers". If you declared "i" outside both code blocks, at the
top level of the method, then you'd have the problem again because both peer
code blocks would themselves be children of the top-level scope, the method
itself.

In practice I usually find myself declaring variables I want to reuse at a
higher scope, e.g.,

public void doSomething() {
int i = 0;
IList list = null;

for (i = 0;i < 5;i++) {
list = new ArrayList();
Console.WriteLine(i / list.Count);
}

i = 23;
list = new ArrayList();
Console.WriteLine(i / list.Count);
}

The problem here is that if you don't assign values when you do the initial
declarations, the compiler often needlessly natters about uninitialized
variables. And of course this isn't as "safe". But methods should be of a
manageable size; if you start losing track of things it needs refactoring
anyway. So I think it's an acceptable trade-off.

--Bob
 
Back
Top