Variable Scope Confusion in C#

Variable Scope in c# confusion

How can a the variable _shouldStop be used before it is declared?

C#, unlike many other languages in the C family, does not require that named entities be declared before they are used. Locals must be declared before they are used, but classes, fields, events, properties, and so on, can be declared and used in any order.

C# also requires that locals be assigned before they are read, but other variables need not be assigned before they are read. Fields, array elements and so on, are assigned a default value when they are created.

Confusion about variable scope in if block

Considering only the scopes your second code is equivalent to this one:

string b = "Foo";
if(...)
{
string b = "Hello";
}
if(...)
{
string b = "World";
}

Here you see that you really have to decarations for b, one in the outer scope and one in the inner. That means it doesn´t matter in which order your declarations occur. Furthermore a scope is never just limited to itself, but to all child-scopes also. So in your case the scope for string b = "Foo" (which seems to be method-scope) contains the entire method, thus also the scope of the if-blocks. The latter however don´t include the scope of the method.

In your first example on the other hand the variables only live within the inner scope.

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.

Variable scope problems C#

instance members (those not marked with the static keyword) exist once per object instance. Every instance of an object gets its own copies of them.

static members, on the other hand, exist once for the entire class and are shared by all object instances.

So, If you've got a class:

class Foo
{
public static int Alpha { get ; set ; }
public int Bravo { get ; set ; }
}

No matter how many instances of your Foo class are created, there is only one instance of Alpha. Any instance method or property can access a static member directly.

Instance members, since they exist on a per-instance basic, require an object instance to reference them. If you add some methods to the Foo class:

public static int DoSomething()
{
return Alpha * 3 ;
}

is perfectly valid — the method is static and the member is static. Ditto for an instance method:

public int DoSomethingElse()
{
return Alpha * 3 ;
}

Something like this will fail:

public static int AndNowForSomethingCompletelyDifferent()
{
return Alpha * 3 + Bravo ;
}

Bravo can't be referenced here without a reference to an instance of Foo. This will work however:

public static int AndNowForSomethingCompletelyDifferent( Foo instance )
{
return Alpha * 3 + instance.Bravo ;
}

As will this:

public int AndNowForSomethingCompletelyDifferent()
{
return Alpha * 3 + Bravo ;
}

Since the method is non-static (an instance method), it has an implicit reference (this) to its instance of Foo. The above is exactly equivalent to

public int AndNowForSomethingCompletelyDifferent()
{
return Alpha * 3 + this.Bravo ;
}

In your case, you could instantiate the class Program in your Main() method:

public static void Main( string[] args )
{
Program p = new Program() ;
p.game() ;
return ;
}

Or you could mark your methods game(), correct() and incorrect() as static, just as the Main() method is marked.

Hope this helps!

c# visibility of variable

It's not accessible because it's declared in an inner scope that is not visible in the outer scope. But you just want to change the SeachOption according to the bool, so why not simply:

SearchOption opt = allDirs ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
IEnumerable<FileInfo> list1 = dir1.GetFiles("*.*", opt);
IEnumerable<FileInfo> list2 = dir2.GetFiles("*.*", opt);
bool areIdentical = list1.SequenceEqual(list2, new FileCompare());

If you couldn't do that you have to declare it outside:

IEnumerable<FileInfo> list1;
IEnumerable<FileInfo> list2;
if(allDirs)
{
list1 = ...
list2 = ...
}
else
{
list1 = ...
list2 = ...
}
// now you can access them here because they are declared in the same scope

C# variable scoping: 'x' cannot be declared in this scope because it would give a different meaning to 'x'

The issue here is largely one of good practice and preventing against inadvertent mistakes. Admittedly, the C# compiler could theoretically be designed such that there is no conflict between scopes here. This would however be much effort for little gain, as I see it.

Consider that if the declaration of var in the parent scope were before the if statement, there would be an unresolvable naming conflict. The compiler simply does not differentiate between the following two cases. Analysis is done purely based on scope, and not order of declaration/use, as you seem to be expecting.

The theoretically acceptable (but still invalid as far as C# is concerned):

if(true)
{
string var = "VAR";
}

string var = "New VAR!";

and the unacceptable (since it would be hiding the parent variable):

string var = "New VAR!";

if(true)
{
string var = "VAR";
}

are both treated precisely the same in terms of variables and scopes.

Now, is there any actual reason in this secenario why you can't just give one of the variables a different name? I assume (hope) your actual variables aren't called var, so I don't really see this being a problem. If you're still intent on reusing the same variable name, just put them in sibling scopes:

if(true)
{
string var = "VAR";
}

{
string var = "New VAR!";
}

This however, while valid to the compiler, can lead to some amount of confusion when reading the code, so I recommend against it in almost any case.



Related Topics



Leave a reply



Submit