Unsure about Something

C

C# Learner

I've been told on here that the following is equivalent:

<snippet 1>

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

</snippet 1>

<snippet 2>

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

</snippet 2>

In the second snippet, I cannot use 'a' outside of the loop. Why?

For example, the following causes a compile error ("The name 'a' does
not exist in the class or namespace"):

<snippet 3>

for (int i = 0; i < 10; ++i) {
int a = GetValueFromSomewhere();
}
a = AnotherValue();

</snippet 3>
 
D

Darrin J Olson

This is as expected. You may want to read a little about variable scope. In
snippet 2, since you declared your variable inside the loop, it's scope is
only inside the loop. In snippet 1, you declared the variable outside of the
loop, so it's scope is inside the loop and inside the current structure,
whether it be another structure or the parent class.

-Darrin
 
D

Daniel O'Connell [C# MVP]

C# Learner said:
I've been told on here that the following is equivalent:

<snippet 1>

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

</snippet 1>

<snippet 2>

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

</snippet 2>

In the second snippet, I cannot use 'a' outside of the loop. Why?
Because you declare a within the scope of the loop. Variable declarations
are scoped within the block they were declared in and are only accessible in
that block and in any underlying block. Its there for protection and correct
coding purposes.
You will see the same result with any block type, including

{
int a = 10;
}
a= 5;
 
S

Sheila Jones

Hi!

C# is a block-scoped language, so local variables are only visible within
the block they are defined in and any sub-blocks within that block. (A block
is delimited by curly braces, { and }). In your second snippet, 'a' is
defined inside the 'for' statement's block so is invisible outside this.

Hope that helps!
 
J

Jeffrey Wynn

This is due to scoping rules in C#. In C# as in many languages (C, C++,
etc.), scope is often visibly delimited within matching curly braces.
Variables that are declared in an outer scope (as "a" in in snippet1), are
accessible to nested or inner scopes, while variables declared in inner
scopes, aren't available to expressions in outer scopes. This is why
snippet2 causes a compilation error. The C# compiler is enforcing this
scoping rule.

Both statements are similar in form and function. The difference comes in
whether or not you need to access the value "GetValueFromSomewhere()" in a
outer scope, such as outside of the for loop.

Hope this helps.
 
B

Bjorn Abelli

...
I've been told on here that the
following is equivalent:

Well, it's only a "half truth" as the examples are equivalent in
"functionality", but there are significant differences "behind the scene"...
<snippet 1>

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

</snippet 1>

In the first case you declare *one* variable outside the "scope" of the
for-loop. In each iteration you give the variable a different value.
<snippet 2>

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

</snippet 2>

In the second case you declare a *new* variable in each iteration.
In the second snippet, I cannot use 'a' outside of the loop. Why?

Because you've gone "out of scope" for the loop, and hence the variable
doesn't exist any more. This applies to all kinds of "scopes", such as
blocks within if-statements, while-loops, etc. It can also be made
explicitly by just adding a pair of curly brackets around anything you want
to make into a separate "scope".

One of the reasons to this is to gain uniformity in the access to locally
declared variables.

I'll try to give you a concrete example, based on your snippets. What if you
had another condition in your for-loop, that could result in that the "body"
of the loop never was entered? In that case the variable would never have
been declared at all, and hence a run-time error would have occured.

This was just a very simplified explanation, but I hope some of the message
gets through... :)

// Bjorn A
 
C

C# Learner

<snip>

Thanks for all the replies.

Just to clarify -- in the following, is a's *lifetime* (as opposed to
its scope) the duration of SomeMethod?

<snippet 1>

void SomeMethod()
{
for (int i = 0; i < 10; ++i) {
int a = GetValueFromSomewhere();
}
}

</snippet 1>

How about in this slightly more complex example?

<snippet 2>

void SomeMethod()
{
for (int i = 0; i < 10; ++i) {
if (something) {
for (int j = 0; j < 10; ++j) {
int a = GetValueFromSomewhere();
}
}
}
}

</snippet 2>

Sorry for all the questions; I just want to ensure understand this
fully.
 
R

Roy Fine

see inline --

C# Learner said:
I've been told on here that the following is equivalent:

<snippet 1>

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

</snippet 1>

<snippet 2>

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

</snippet 2>

In the second snippet, I cannot use 'a' outside of the loop. Why?

That is the way the language is defined: (8.5.1) "The scope of a local
variable declared in a local variable declaration is the block in which the
declaration occurs". And block is defined (2.2.1) as either { } or {
statement list }. And scope of a name is defined (3.7) as "the region of
program text within which it is possible to refer to an entity declared by
the name without qualification of the name"

Here is a link to an interesting article that explains all of this in much
more detail... :)

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/CSharpSpecStart.asp


Snip 1 and Snip 2 are equivalent ONLY because there is no attempted access
to variable "a" outside the block. In this very simple example, where the
variable "a" is declared is a matter of style.

However, and this is to the last point you raise, if you want to have access
to the variable that is used inside the block of code (whether it be before
or after the execution of the block), then that variable MUST be in scope
for the outer block.

regards
roy fine
 
B

Bjorn Abelli

<snip>

Thanks for all the replies.

Just to clarify -- in the following, is a's
*lifetime* (as opposed to
its scope) the duration of SomeMethod?

<snippet 1>

void SomeMethod()
{
for (int i = 0; i < 10; ++i) {
int a = GetValueFromSomewhere();
}
}

</snippet 1>

Yes, a ceases to exist when you leave SomeMethod, but not *opposed* to, it
*is* the scope (marked by the curly brackets).
How about in this slightly more complex example?

<snippet 2>

void SomeMethod()
{
for (int i = 0; i < 10; ++i) {
if (something) {
for (int j = 0; j < 10; ++j) {
int a = GetValueFromSomewhere();
}
}
}
}

</snippet 2>

"a" goes out of scope for *each* iteration in the inner-most loop.

// Bjorn A
 
B

Bjorn Abelli

:



Okay, but does _a_ get re-created each time the "for (int j..." loop
is entered, or is it created only once, when SomeMethod() is entered?

In each iteration you have declared a new variable with the name "a". This
variable is "destroyed" for each turn, so in that sense you can say that
it's "re-created" (though it's not the same variable, but a "new" variable
for each iteration).

// Bjorn A
 
C

C# Learner

In each iteration you have declared a new variable with the name "a". This
variable is "destroyed" for each turn, so in that sense you can say that
it's "re-created" (though it's not the same variable, but a "new" variable
for each iteration).

Hi,

I've previously tried Googling on this subject and have found that
some say the above is true and some say the opposite is true.

i.e.:

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

Some say that _n_ is only *created* once, whereas others say that it
is re-created on each iteration of the loop.

So in the former case, the integer variable _n_ would be created once.
In the latter, 10 integer variables would eventually be created; one
for each iteration of the loop.

I'm still not sure which side to believe...

I'd like to use the whole declaration inside the loop, for
readability, instead of declaring the int outside the loop once then
using it in the loop. However, if this is going to cost in terms of
performance I wouldn't want to do it.
 
R

Richard A. Lowe

Well, form a purely IL (intermediate language) perspective the variable 'n'
IS being created with a *method-wide* scope level.

The simple example:
for (int i = 0; i < 10; ++i) {
int n = i;
}

produces the following relevant IL:
..method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init ([0] int32 i,
[1] int32 n)

The ".locals init" line defines two int32's at the start of the method.
Indeed, I don't think there is any other way to do this (declare a local
variable) in IL - so enforcing scope more granular than method level is a
job for the compiler.

Richard
 
N

Nick Malik

The performance issues will be handled by the compiler within reason.
As a matter of style, you would be safe to declare your variable with the
most restrictive possible scope within the module.

Note, however, that if you declare a variable in a Try block, it will not be
available in the matching Catch block for exception handling.

Personally, I usually declare my variables outside of loops, especially if
they are reference variables... but that's just me.

Good Luck,
--- Nick
 
C

C# Learner

Richard A. Lowe said:
Well, form a purely IL (intermediate language) perspective the variable 'n'
IS being created with a *method-wide* scope level.

The simple example:
for (int i = 0; i < 10; ++i) {
int n = i;
}

produces the following relevant IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init ([0] int32 i,
[1] int32 n)

The ".locals init" line defines two int32's at the start of the method.
Indeed, I don't think there is any other way to do this (declare a local
variable) in IL - so enforcing scope more granular than method level is a
job for the compiler.

Richard

Cool. You've opened my eyes to ILDASM.exe :)

The above holds true for my slightly more complex code too.

With this knowledge, I think I'll stick to using inner-most scope
variable declarations.

Thanks for all the replies!
 
J

Justin Rogers

Note, inner scoped variables result in larger stack frames. ILDasm the
following code:

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

This will result in 3 separate locals:
V_0, V_1, V_2 if compiling release and
i, V_0, V_1 if compiling debug

You'd think the compiler might re-use the inner scoped variable but it doesn't.
The same applies
if you add an int n inside each of the loops. The code will compile, but it
will result in 6 locals. I'm
not sure how things will behave if you /optimize, but it might be worth a look.
I'll leave it up to you
to ILDasm that option for yourself.

Morale: Sometimes the smallest repro scenario isn't accurate enough on a global
scale to make a
decision about how you should allocate your resources. Clearly, if you have
multiple loops, it would
make sense from a stack size perspective to simply re-use a single integer local
rather than using the
inner definition form.

--
Justin Rogers
DigiTec Web Consultants, LLC.
Blog: http://weblogs.asp.net/justin_rogers

C# Learner said:
Richard A. Lowe said:
Well, form a purely IL (intermediate language) perspective the variable 'n'
IS being created with a *method-wide* scope level.

The simple example:
for (int i = 0; i < 10; ++i) {
int n = i;
}

produces the following relevant IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init ([0] int32 i,
[1] int32 n)

The ".locals init" line defines two int32's at the start of the method.
Indeed, I don't think there is any other way to do this (declare a local
variable) in IL - so enforcing scope more granular than method level is a
job for the compiler.

Richard

Cool. You've opened my eyes to ILDASM.exe :)

The above holds true for my slightly more complex code too.

With this knowledge, I think I'll stick to using inner-most scope
variable declarations.

Thanks for all the replies!
 
C

C# Learner

Justin Rogers said:
Note, inner scoped variables result in larger stack frames. ILDasm the
following code:

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

This will result in 3 separate locals:
V_0, V_1, V_2 if compiling release and
i, V_0, V_1 if compiling debug

You'd think the compiler might re-use the inner scoped variable but it doesn't.
The same applies
if you add an int n inside each of the loops. The code will compile, but it
will result in 6 locals. I'm
not sure how things will behave if you /optimize, but it might be worth a look.
I'll leave it up to you
to ILDasm that option for yourself.

With the /optimize flag set, the result is still 6 locals:

<C# code>

for(int i = 0; i < 10; i++) {
int n = i;
Console.WriteLine(n); // stop n from being optimized out
}
for(int i = 0; i < 10; i++) {
int n = i;
Console.WriteLine(n); // stop n from being optimized out
}
for(int i = 0; i < 10; i++) {
int n = i;
Console.WriteLine(n); // stop n from being optimized out
}

</C# code>

<relevant IL code>

..locals init ([0] int32 i,
[1] int32 n,
[2] int32 V_2,
[3] int32 V_3,
[4] int32 V_4,
[5] int32 V_5)

</relevant IL code>

Perhaps the optimizer implementors decided that optimization, in this
case, should be a human task, considering the logic.
Morale: Sometimes the smallest repro scenario isn't accurate enough on a global
scale to make a
decision about how you should allocate your resources. Clearly, if you have
multiple loops, it would
make sense from a stack size perspective to simply re-use a single integer local
rather than using the
inner definition form.

Thanks for the info!
 
B

Bjorn Abelli

"Bjorn Abelli" wrote:
I've previously tried Googling on this subject and have found that
some say the above is true and some say the opposite is true.

i.e.:

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

Some say that _n_ is only *created* once, whereas others
say that it is re-created on each iteration of the loop.

Well, now you're into compiler optimizations, where it in those examples
very likely would result in only one variable.

But as that optimization is dependant on the compiler, there can actually be
no guarantee for that behaviour, it could in future compilers from e.g.
third-party vendors give other results. Though it is "likely" for any
compiler to have variable elimination as a basic optimization, I haven't
seen any "guarantees" to enforce that kind of optimization.

But even if the compiler optimizes the code in this way, the variable in
your example still "goes out of scope" and won't be reachable in outer or
following scopes.

// Bjorn A
 
J

Justin Rogers

I figured I'd investigate more and blog about this. I think there is a
limitation in the C# compiler, since ILAsm has the concept of locally scoped
variables. You can view my blog comments about this at:

http://weblogs.asp.net/justin_rogers/archive/2004/02/16/73627.aspx

However, I think even the ILAsm version isn't as optimized as it could be, as I
note in the entry.


--
Justin Rogers
DigiTec Web Consultants, LLC.
Blog: http://weblogs.asp.net/justin_rogers

Justin Rogers said:
Note, inner scoped variables result in larger stack frames. ILDasm the
following code:

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

This will result in 3 separate locals:
V_0, V_1, V_2 if compiling release and
i, V_0, V_1 if compiling debug

You'd think the compiler might re-use the inner scoped variable but it doesn't.
The same applies
if you add an int n inside each of the loops. The code will compile, but it
will result in 6 locals. I'm
not sure how things will behave if you /optimize, but it might be worth a look.
I'll leave it up to you
to ILDasm that option for yourself.

Morale: Sometimes the smallest repro scenario isn't accurate enough on a global
scale to make a
decision about how you should allocate your resources. Clearly, if you have
multiple loops, it would
make sense from a stack size perspective to simply re-use a single integer local
rather than using the
inner definition form.

--
Justin Rogers
DigiTec Web Consultants, LLC.
Blog: http://weblogs.asp.net/justin_rogers

C# Learner said:
Richard A. Lowe said:
Well, form a purely IL (intermediate language) perspective the variable 'n'
IS being created with a *method-wide* scope level.

The simple example:
for (int i = 0; i < 10; ++i) {
int n = i;
}

produces the following relevant IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init ([0] int32 i,
[1] int32 n)

The ".locals init" line defines two int32's at the start of the method.
Indeed, I don't think there is any other way to do this (declare a local
variable) in IL - so enforcing scope more granular than method level is a
job for the compiler.

Richard

Cool. You've opened my eyes to ILDASM.exe :)

The above holds true for my slightly more complex code too.

With this knowledge, I think I'll stick to using inner-most scope
variable declarations.

Thanks for all the replies!
 
M

Martin Maat

Justin Rogers said:
Note, inner scoped variables result in larger stack frames. ILDasm the
following code:
You'd think the compiler might re-use the inner scoped
variable but it doesn't. The same applies if you add an
int n inside each of the loops. The code will compile,
but it will result in 6 locals.

Let's not forget we are still talking IL here, with an underscored I. The
IL-to-native compiler is likely to optimize these constructs a bit further
so looking at IL is nice but I don't think it says a whole lot in this
particular case.

It would not be too hard for a compiler to determine that variable x is used
until line 8 after which it loses relevance. The location occupied by it (be
it on the stack or in a processor register) may be recycled after that. You
don't know what ultimately goes on at the lowest level, neither does the
IL-compiler so it is best for the IL-compiler to do things in a
straight-forward and recognizable manner instead of trying to "optimize",
only obfuscating it for the next step and making it harder to gain
performance.

C# Learner> Perhaps the optimizer implementors decided
C# Learner> that optimization, in this case, should be a
C# Learner> human task, considering the logic.

It is, it is the tast of the IL-to-native compiler writers.

Martin.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top