Newbie Q: Declare variable IN the loop or BEFORE the loop?

R

Rex

Hi All,
I have a somewhat trivial newbie question - but I'm trying to "get" C#
more and more. Here goes: Let's say I have a loop:

for (int i = 0;i <= MyCollection.Count - 1;i++)
{
int x = i + 2;
// do whatever, some code using i and some code using x (which
is always 2 greater than i)
}

Now, coming from a traditional VB 6 background, I would normally
declare x BEFORE the loop, and then use "x = i + 2:" within the loop.
Because it seems "wasteful" to keep declaring a NEW variable x WITH
EVERY ITERATION of the loop.

Is it indeed "wasteful" to declare x as the code shows above?
And - maybe more importantly - what is STANDARD PRACTICE in the C#
world in this case?

Thanks much,

Rex
 
J

Jon Skeet [C# MVP]

I have a somewhat trivial newbie question - but I'm trying to "get" C#
more and more. Here goes: Let's say I have a loop:

for (int i = 0;i <= MyCollection.Count - 1;i++)
{
int x = i + 2;
// do whatever, some code using i and some code using x (which
is always 2 greater than i)
}

Now, coming from a traditional VB 6 background, I would normally
declare x BEFORE the loop, and then use "x = i + 2:" within the loop.
Because it seems "wasteful" to keep declaring a NEW variable x WITH
EVERY ITERATION of the loop.

You say it *seems* wasteful - I'm not familiar enough with VB6 to know
whether it actually *is* wasteful in that concext.
Is it indeed "wasteful" to declare x as the code shows above?
No.

And - maybe more importantly - what is STANDARD PRACTICE in the C#
world in this case?

Declare a variable with as little scope as possible, generally. It
makes it clear that you're not intending to use the variable outside
that scope.

Jon
 
M

Marc Gravell

The IL will be unaffected (IL variables are all declared before the
code) - so no, it isn't any more wasteful. There is only one real (IL)
variable that gets re-used (either way).

As such, I prefer to declare *inside* the loop under the "limit the
scope whenever possible" approach; note that the only time there is a
functional difference is when you assign the variable outside of the
loop, and / or (more importantly) use the existing value without
assigning it first - i.e. there is a huge difference between

int i = 0;
for {
i++;
}
and
for {
int i = 0;
i++;
}

In one case i is always 1, in t'other it increments gradually.

Marc
 
J

Jon Skeet [C# MVP]

The IL will be unaffected (IL variables are all declared before the
code) - so no, it isn't any more wasteful. There is only one real (IL)
variable that gets re-used (either way).

As such, I prefer to declare *inside* the loop under the "limit the
scope whenever possible" approach; note that the only time there is a
functional difference is when you assign the variable outside of the
loop, and / or (more importantly) use the existing value without
assigning it first - i.e. there is a huge difference between

There's one other difference, which relates to the variable's
*lifetime* as opposed to just its scope, with respect to captured
variables in anonymous methods. As such, just the other day I had a
loop like this:

foreach (string url in someListOfUrls)
{
string copyOfUrl = url;
ThreadStart ts = delegate() { DoSomethingWith(copyOfUrl); };
new Thread(ts).Start();
}

At first glance, you'd expect to be able to remove copyOfUrl - but the
lifetime of url is the lifetime of the whole loop, and thus shared
between all the ThreadStart delegates, whereas the lifetime of
copyOfUrl is just that iteration of the loop. Declaring copyOfUrl
outside the loop would defeat its purpose.

Jon
 
R

Rex

Jon and Marc - THANKS! Great discussion - Definitely cleared things up
for me. Best to both of you, Rex
 
C

cjard

Now, coming from a traditional VB 6 background, I would normally
declare x BEFORE the loop, and then use "x = i + 2:" within the loop.
Um, partly thats because VB6 doesnt understand the concept of scope at
all.. The following code in VB6 crashes:

If someTest Then
Dim x as Integer
Set x = 1
End If

If anotherTest Then
Dim x as String 'fails.. a variable called x is already declared
End If

So you were taught to Dim all your variables at the top of the
procedure.. Its what VB6 does anyway.. it dims everything at procedure
start whether it will be used or not. Lame language. :)
Is it indeed "wasteful" to declare x as the code shows above?

Not really, because modern compilers are intelligent enough to see
that variables declared inside a loop dont need destroying and
recreating millions of times. Once upon a time it was a dumb thing to
do, because the var WOULD be recycled and it was a huge waste of CPU
resources. Now, as Jon says, knowing that the compiler will
automatically create and re-use one variable only, you can program it
with respect for the sensibilities of scope, rather than to avoid a
performance trap
Because it seems "wasteful" to keep declaring a NEW variable x WITH
EVERY ITERATION of the loop.
If it looks like cheese and it cmells like cheese - it might not be
cheese! :) I invite you to test the performance of this yourself..
Do two loops of a billion iterations, one assigns a in-loop created
var, one reassigns an out-loop created var.. see if there s a
difference?

(And if there is,report it to MS)
 
P

Peter Duniho

[...]
foreach (string url in someListOfUrls)
{
string copyOfUrl = url;
ThreadStart ts = delegate() { DoSomethingWith(copyOfUrl); };
new Thread(ts).Start();
}

At first glance, you'd expect to be able to remove copyOfUrl - but the
lifetime of url is the lifetime of the whole loop, and thus shared
between all the ThreadStart delegates, whereas the lifetime of
copyOfUrl is just that iteration of the loop. Declaring copyOfUrl
outside the loop would defeat its purpose.

Actually, while I don't doubt that the above was a problem (especially
since you specifically say you ran into a real-world example, I'm not
really clear on *why* it's a problem.

The variable is being passed by value, so I don't understand why it's a
problem to use "url" rather than "copyOfUrl". Wouldn't the current value
of "url" be passed to the delegate, without having the lifetime of the
variable "url" matter?

I could see how it would be a problem with a "ref" or "out" parameter, but
I don't see how the example you gave is problematic.

Typically, in a situation like this, I'm just not getting something about
the example. That's probably the case here too. :) But I'd appreciate
it if you could help me out here...I assume that given the context of this
thread, if I'm confused, your answer may help other new C# programmers too.

Thanks,
Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
Actually, while I don't doubt that the above was a problem (especially
since you specifically say you ran into a real-world example, I'm not
really clear on *why* it's a problem.

The variable is being passed by value, so I don't understand why it's a
problem to use "url" rather than "copyOfUrl". Wouldn't the current value
of "url" be passed to the delegate, without having the lifetime of the
variable "url" matter?

url is being passed to the DoSomethingWith method at the time that the
delegate is executed - by which time it may not (and indeed didn't, in
real life) have the same value as when the delegate was created.
I could see how it would be a problem with a "ref" or "out" parameter, but
I don't see how the example you gave is problematic.

Typically, in a situation like this, I'm just not getting something about
the example. That's probably the case here too. :) But I'd appreciate
it if you could help me out here...I assume that given the context of this
thread, if I'm confused, your answer may help other new C# programmers too.

Sure - it's probably easiest just to refer you to a page where I've
taken the time to explain it properly. However, any questions you have
after reading that, I'll very happily answer. (Indeed, that would be
ideal for me - I'm writing about delegates next, so it's good to find
out what people find confusing!)

http://pobox.com/~skeet/csharp/csharp2/delegates.html#anonymous.methods
 
A

Aneesh Pulukkul[MCSD.Net]

Um, partly thats because VB6 doesnt understand the concept of scope at
all.. The following code in VB6 crashes:

If someTest Then
Dim x as Integer
Set x = 1
End If

If anotherTest Then
Dim x as String 'fails.. a variable called x is already declared
End If

So you were taught to Dim all your variables at the top of the
procedure.. Its what VB6 does anyway.. it dims everything at procedure
start whether it will be used or not. Lame language. :)


Not really, because modern compilers are intelligent enough to see
that variables declared inside a loop dont need destroying and
recreating millions of times. Once upon a time it was a dumb thing to
do, because the var WOULD be recycled and it was a huge waste of CPU
resources. Now, as Jon says, knowing that the compiler will
automatically create and re-use one variable only, you can program it
with respect for the sensibilities of scope, rather than to avoid a
performance trap


If it looks like cheese and it cmells like cheese - it might not be
cheese! :) I invite you to test the performance of this yourself..
Do two loops of a billion iterations, one assigns a in-loop created
var, one reassigns an out-loop created var.. see if there s a
difference?

(And if there is,report it to MS)

Nice info provided. You guys rock!!
 
P

Peter Duniho

url is being passed to the DoSomethingWith method at the time that the
delegate is executed - by which time it may not (and indeed didn't, in
real life) have the same value as when the delegate was created.

Ahh, I see. Because of the "capturing" behavior, the variable itself is
what's referenced until the point at which the delegate actually starts
executing. At which point, the value of the variable *is* passed to the
delegate, but it's value may well have changed by that time. Okay,
thanks. I think I've got it now.

I have to admit, while I find the anonymous delegate syntax convenient, it
sure introduces some weird behaviors. It would be less convenient to have
to use a parameterized thread start, and to create an explicit method to
use for the delegate, but only slightly so and it would avoid the somewhat
(to me, anyway) non-intuitive "capture" behavior.

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
Ahh, I see. Because of the "capturing" behavior, the variable itself is
what's referenced until the point at which the delegate actually starts
executing.

Even after that - it can be executed multiple times, for instance. The
value passed to the method is "fixed" at the point when the
DoSomethingWith method is called, in the normal way, but "url" is still
a variable.
At which point, the value of the variable *is* passed to the
delegate, but it's value may well have changed by that time. Okay,
thanks. I think I've got it now.

Goodo :)
I have to admit, while I find the anonymous delegate syntax convenient, it
sure introduces some weird behaviors. It would be less convenient to have
to use a parameterized thread start, and to create an explicit method to
use for the delegate, but only slightly so and it would avoid the somewhat
(to me, anyway) non-intuitive "capture" behavior.

Yup. I've just been reading about some even more curious behaviour
(described using lambda expressions, but still relevant to "normal"
anonymous methods, I believe):

http://blogs.msdn.com/ericlippert/archive/2007/06/06/fyi-c-and-vb-
closures-are-per-scope.aspx
 
P

Peter Duniho

Even after that - it can be executed multiple times, for instance. The
value passed to the method is "fixed" at the point when the
DoSomethingWith method is called, in the normal way, but "url" is still
a variable.

As in the case of an event handler, for example? Basically, as long as
the delegate exists, the variable is captured. Every time the delegate is
called, the value of the variable (whatever it happens to be at that
moment) is passed to the delegate. Right?
Yup. I've just been reading about some even more curious behaviour
(described using lambda expressions, but still relevant to "normal"
anonymous methods, I believe):

http://blogs.msdn.com/ericlippert/archive/2007/06/06/fyi-c-and-vb-
closures-are-per-scope.aspx

Great. :)

I would buy some of the arguments against incorporating potentially
confusing features in C# if only the language did a better job of keeping
other potentially confusing features out. :) I see the usefulness in
stuff like this, but it seems silly to rail against multiple inheritence
(for example, and to pick one that I'm actually just fine with not being
in C#) when you've got stuff like this running around already.

Pete
 
P

Peter Duniho

[...]
Yup. I've just been reading about some even more curious behaviour
(described using lambda expressions, but still relevant to "normal"
anonymous methods, I believe):

http://blogs.msdn.com/ericlippert/archive/2007/06/06/fyi-c-and-vb-closures-are-per-scope.aspx

Having now had a chance to read that, I'm even more strongly wondering why
they didn't just include multiple inheritance and be done with it. :)

I'm only just getting to the point where I feel like I understand more
than the mere basics of anonymous methods, and now they're introducing
this "lambda" thing. I admit, I'm going to have to read the page you
referenced a few more times before I really understand why an anonymous
delegate works differently from a lamba expression in that case, but I'm
curious as to what methods are available for working around the issue Eric
mentions. In particular, would it be sufficient to include the
"shortlived" declaration and use in a new code block?

I have gathered from past discussions that the answer is "no", because
even though you can change scope with code blocks, you can't change
lifetime (in particular, all locals in a method have the same lifetime,
assuming no other funny business like capturing). But I just want to
verify that, or find out if my assumption is wrong.

To make matters even more complicated, I note this post also by Eric:
http://blogs.msdn.com/ericlippert/a...xpressions-vs-anonymous-methods-part-one.aspx

In which he describes a subtle difference in how the compiler handles
anonymous delegates versus lambda expressions.

Ugh. What does C# want to be? Simple? Or powerful? :(

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
As in the case of an event handler, for example? Basically, as long as
the delegate exists, the variable is captured. Every time the delegate is
called, the value of the variable (whatever it happens to be at that
moment) is passed to the delegate. Right?

Spot on. And of course the delegate instance could change the value of
the variable too, were it so coded - and any other delegate instances
sharing that captured variable would see the changes.
Great. :)

I would buy some of the arguments against incorporating potentially
confusing features in C# if only the language did a better job of keeping
other potentially confusing features out. :) I see the usefulness in
stuff like this, but it seems silly to rail against multiple inheritence
(for example, and to pick one that I'm actually just fine with not being
in C#) when you've got stuff like this running around already.

I see what you mean, but I think anonymous methods scare people off
more than multiple inheritance would. (I mean that people would happily
abuse MI, but after a few run-ins with this kind of thing, they're
cautious with anonymous methods.) Purely a guess though.

LINQ relies on lambda expressions which have the same problem though -
and the benefits of closures in general (and lambda expressions in
particular) are massive, IMO. I think they're going to have a much
bigger benefit than MI would.
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
[...]
Yup. I've just been reading about some even more curious behaviour
(described using lambda expressions, but still relevant to "normal"
anonymous methods, I believe):

http://blogs.msdn.com/ericlippert/archive/2007/06/06/fyi-c-and-vb-c
losures-are-per-scope.aspx

Having now had a chance to read that, I'm even more strongly wondering why
they didn't just include multiple inheritance and be done with it. :)

I'm only just getting to the point where I feel like I understand more
than the mere basics of anonymous methods, and now they're introducing
this "lambda" thing. I admit, I'm going to have to read the page you
referenced a few more times before I really understand why an anonymous
delegate works differently from a lamba expression in that case

I don't *think* it does - but I'll check later what gets compiled, and
post it then. (Can't do it just now.)
but I'm curious as to what methods are available for working around
the issue Eric mentions. In particular, would it be sufficient to
include the "shortlived" declaration and use in a new code block?

I believe so, yes.
I have gathered from past discussions that the answer is "no", because
even though you can change scope with code blocks, you can't change
lifetime (in particular, all locals in a method have the same lifetime,
assuming no other funny business like capturing). But I just want to
verify that, or find out if my assumption is wrong.

Lifetime is only relevant with capturing, which is relevant here
because the lambda expressions capture the variables.
To make matters even more complicated, I note this post also by Eric:
http://blogs.msdn.com/ericlippert/a...xpressions-vs-anonymous-methods-part-one.aspx

In which he describes a subtle difference in how the compiler handles
anonymous delegates versus lambda expressions.

Haven't read that yet. Will soon.
Ugh. What does C# want to be? Simple? Or powerful? :(

:)
 
P

Peter Duniho

[...]
but I'm curious as to what methods are available for working around
the issue Eric mentions. In particular, would it be sufficient to
include the "shortlived" declaration and use in a new code block?

I believe so, yes.

You believe it _would_ be sufficient? Then perhaps I misunderstand the
lifetime issues. After that statement, I wrote (and you quoted)...
Lifetime is only relevant with capturing, which is relevant here
because the lambda expressions capture the variables.

Lifetime is relevant then, and so the queston is...can you affect lifetime
by using a new statement block? I thought the answer was "no", but
perhaps I was wrong. A variable that is scoped within a new statement
block, does its lifetime end at the end of that block? Or does it have a
lifetime equal to the containing method? (Capturing aside, of course).

Pete
 
J

Jon Skeet [C# MVP]

Jon Skeet said:
I don't *think* it does - but I'll check later what gets compiled, and
post it then. (Can't do it just now.)

Just checked - yes it does. Use ildasm on this code:

using System;
using System.Threading;

class Test
{
static void Main()
{
int x = 5;
int y = 10;

ThreadStart a = delegate() { Console.WriteLine (x); };
ThreadStart b = delegate() { Console.WriteLine (y); };
}
}

You'll find that both a and b *actually* have access to both x and y.
Haven't read that yet. Will soon.

Read it now. Not too bothered - it may affect what error messages are
produced, but it shouldn't need to bother the average C# developer.
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
[...]
but I'm curious as to what methods are available for working around
the issue Eric mentions. In particular, would it be sufficient to
include the "shortlived" declaration and use in a new code block?

I believe so, yes.

You believe it _would_ be sufficient?

I did - but I didn't have the example in front of me, unfortunately,
and probably wasn't thinking clearly enough. It's the combined scope of
the captured variable and the delegate which is relevant.

Here's Eric's example in a slightly simpler form. I'm using ThreadStart
as a simple parameterless/returnless delegate type - nothing more than
that. Likewise I'm just using string and Stream as random different
types. (Yes, you'd really want to dispose of a stream, etc - irrelevant
to this discussion. Let me know if it bothers you.)

using System;
using System.IO;
using System.Threading;

class Test
{
static void Main()
{
}

static ThreadStart Foo()
{
string cheap = "cheap";
Stream expensive = null;

ThreadStart shortLived = delegate()
{ Console.WriteLine (expensive); };
shortLived();

ThreadStart longLived = delegate()
{ Console.WriteLine (cheap); };
return longLived;
}
}

The problem is that the generated code uses a class <>c__Displayclass2
which has captured *both* "cheap" and "expensive", and longLived has a
reference to an instance of that class.

What we want is for longLived to *only* have an indirect reference to
cheap, not to expensive. It turns out that by adding one level of
scoping, this is achieved:

using System;
using System.IO;
using System.Threading;

class Test
{
static void Main()
{
}

static ThreadStart Foo()
{
string cheap = "cheap";
{
Stream expensive = null;

ThreadStart shortLived = delegate()
{ Console.WriteLine (expensive); };
shortLived();
}
ThreadStart longLived = delegate()
{ Console.WriteLine (cheap); };
return longLived;
}
}

Note that this *isn't* just putting shortLived in its own scope - it's
also changing the scope of expensive. Move the brace to below expensive
and the problem is still there. Apologies if that was the cause of
confusion.
Then perhaps I misunderstand the
lifetime issues. After that statement, I wrote (and you quoted)...


Lifetime is relevant then, and so the queston is...can you affect lifetime
by using a new statement block?
Absolutely.

I thought the answer was "no", but
perhaps I was wrong. A variable that is scoped within a new statement
block, does its lifetime end at the end of that block? Or does it have a
lifetime equal to the containing method? (Capturing aside, of course).

Capturing aside, its lifetime ends at the end of that block. But
capturing is the important part. I suspect I may be using lifetime in
far too woolly a sense at the moment, for which I apologise.

I haven't looked into the exact rules in the spec - and that should
probably be your next port of call, rather than my interpretation of
them, but I suspect the rules are something like:

1) Consider each variable which is captured within a method (even if
they're not captured by the same delegates).
2) For each distinct scope which declares a captured variable, create a
class to represent all captured variables declared in that scope.
"Inner" scopes have references to instances of the classes representing
the "next scope out" (only considering the ones which declare captured
variables).
3) When creating a delegate referencing a captured variable, create an
instance method within the nearest scope to the declaration of the
delegate.

Does that make any kind of sense? (I'm really, really interested in how
best to explain this...)
 

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