473,507 Members | 12,744 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Anoynmous methods and variable life time extension - bug orintendend?

Hello everyone,

I experience some strange behaviour of anoynmous delegates which refer
to variables outside the scope of the delegate.

Please have a look at the following code and output at the end of this
post and tell me, if the observed behaviour is intentionally or if it is
a bug.

The problem ist the output in the first foreach loop. It seems, that
every created delegate is executed with the _last_ value of val instead
of the value of val when the delgate is created.

As you can see in the output below, in the second foreach where the
delegate is created in its own method, the behaviour is as I expected it
to be.

Did I misunderstand the following part of the documentation?

| Unlike local variables, the lifetime of the outer variable extends until
| the delegates that reference the anonymous methods are eligible for
| garbage collection. A reference to n is captured at the time the
| delegate is created.

Does this really mean, that a reference to the variable is captured and
not a reference to the value? Why then does it work when putting the
delegate creation in its own method?

Please, could anyone comment on this?

Best regards,
Martin Carpella


class Program {
static void Main(string[] args) {
object semaphore = new object();
lock (semaphore) {
string[] values = new string[] { "a", "b", "c" };

foreach (string val in values) {
ThreadPool.QueueUserWorkItem(delegate(object state)
{
lock (semaphore) {
Console.Out.WriteLine("Pool (1): " + val);
} // lock
});
Console.Out.WriteLine("Main (1): " + val);
} // foreach

foreach (string val in values) {
QueueItem(semaphore, val);
Console.Out.WriteLine("Main (2): " + val);
} // foreach
} // foreach

}

private static void QueueItem(object semaphore, string val) {
ThreadPool.QueueUserWorkItem(delegate(object state) {
lock (semaphore) {
Console.Out.WriteLine("Pool (2): " + val);
} // lock
});
}

}

The strange thing is the output:
Main (1): a
Main (1): b
Main (1): c
Main (2): a
Main (2): b
Main (2): c
Pool (1): c
Pool (1): c
Pool (1): c
Pool (2): a
Pool (2): b
Pool (2): c
May 8 '06 #1
17 1490
On Mon, 08 May 2006 10:24:01 +0200, Martin Carpella wrote:
I experience some strange behaviour of anoynmous delegates which refer
to variables outside the scope of the delegate.

Please have a look at the following code and output at the end of this
post and tell me, if the observed behaviour is intentionally or if it is
a bug.

The problem ist the output in the first foreach loop. It seems, that
every created delegate is executed with the _last_ value of val instead
of the value of val when the delgate is created.


See Jon Skeet explanation about Captured Variables here:
<http://www.yoda.arachsys.com/csharp/csharp2/delegates.html>
May 8 '06 #2
Mehdi <vi****@REMOVEME.gmail.com> writes:
See Jon Skeet explanation about Captured Variables here:
<http://www.yoda.arachsys.com/csharp/csharp2/delegates.html>


Thanks a lot, that cleared things up ;) Its not really intuitive but I
can live with this ;)

Thanks again,
Martin
May 8 '06 #3
"Mehdi" <vi****@REMOVEME.gmail.com> a écrit dans le message de news:
tx****************************@40tude.net...

| See Jon Skeet explanation about Captured Variables here:
| <http://www.yoda.arachsys.com/csharp/csharp2/delegates.html>

Fascinating stuff!! I *think* I understand it but, can someone give me a
real world but simple example of a useful use for this "feature" :-)

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
May 8 '06 #4
Joanna Carter [TeamB] <jo****@not.for.spam> wrote:
| See Jon Skeet explanation about Captured Variables here:
| <http://www.yoda.arachsys.com/csharp/csharp2/delegates.html>

Fascinating stuff!! I *think* I understand it but, can someone give me a
real world but simple example of a useful use for this "feature" :-)


I haven't used C# 2.0 much in anger yet, but Groovy has a very similar
notion with closures. Because the closures don't need a separate
definition (unlike delegates) you can use them much more widely without
introducing a load of extra types. (This comes at the cost of compile-
time safety, admittedly.)

This makes it really easy to do a lot of things. For instance, here's a
sample Groovy script to print out a file and its line numbers:

line=0
new File ("test.groovy").eachLine {
line++
println "$line: $it"
}
The "using" statement in C# is effectively a very specific type of
closure - Groovy applies the concept very widely. C#/.NET 2.0 allows
similar things to let you do something which each element in a list
etc, but it's not quite as straight-forward as in Groovy.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
May 10 '06 #5
"Joanna Carter [TeamB]" <jo****@not.for.spam> wrote:
"Mehdi" <vi****@REMOVEME.gmail.com> a écrit dans le message de news:
tx****************************@40tude.net...

| See Jon Skeet explanation about Captured Variables here:
| <http://www.yoda.arachsys.com/csharp/csharp2/delegates.html>

Fascinating stuff!! I *think* I understand it but, can someone give me a
real world but simple example of a useful use for this "feature" :-)


I use closures all the time for parameterising classes where creating a
new class is more trouble than it's worth.

For example, I have a Resource class which implements IDisposable. Its
constructor takes in a delegate which takes no arguments. This delegate
gets called when the class gets disposed. Here's a quick 2-minute
rewrite with no error checking etc.:

---8<---
delegate void Method();

class Resource : IDisposable
{
private Method _onDispose;

public Resource(Method onDispose)
{
_onDispose = onDispose;
}

public void Dispose()
{
_onDispose();
}

public static Resource ReaderLock(ReaderWriterLock rw,
TimeSpan timeout)
{
rw.AcquireReaderLock(timeout);
return new Resource(delegate { rw.ReleaseReaderLock(); });
}
}
--->8---

I can then write methods anywhere in my framework that can return an
object to be used in a using() block wherever these acquire-release
semantics are used.

Another usage is that it is sometimes actually faster to pass a delegate
than it is to code "outside" the class:

---8<---
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;

class App
{
delegate void Method();

static void Benchmark(string label, int iterations, Method block)
{
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < iterations; ++i)
block();
Console.WriteLine("{0}: {1:f3}", label, watch.ElapsedTicks /
(double) Stopwatch.Frequency);
}

static void Main()
{
List<int> list = new List<int>();
Random r = new Random();

int iterations = 2000;

for (int i = 0; i < 300000; ++i)
list.Add(r.Next());

Benchmark("Using for-loop: ", iterations, delegate
{
int total = 0;
for (int i = 0; i < list.Count; ++i)
total += list[i];
});

Benchmark("Using foreach: ", iterations, delegate
{
int total = 0;
foreach (int x in list)
total += x;
});

Benchmark("Using ForEach: ", iterations, delegate
{
int total = 0;
list.ForEach(delegate(int x) { total += x; });
});
}
}
--->8---

This produces the following output on my machine (Athlon 3500+):

Using for-loop: : 2.902
Using foreach: : 5.044
Using ForEach: : 2.854

-- Barry
May 10 '06 #6
Barry Kelly <ba***********@gmail.com> wrote:

Closures are my favourite feature of C# 2.0. I can live without
generics, but closures have been something I've been looking for in
mainstream languages for far too long. Anonymous delegates aren't
identical to closures because they track the object variables
themselves, rather than the object values at construction time, but
they're a good simulacrum in most practical situations. If you really
need the copy semantics, it's trivial to put a {} block outside the
delegate constructor, and explicitly copy all the variables to capture
to locals declared inside this block.

I think I should add a commentary, just to make a few observations.
Benchmark("Using for-loop: ", iterations, delegate
{
int total = 0;
for (int i = 0; i < list.Count; ++i)
total += list[i];
Here, list is a reference to the captured list, so it is an instance
field in the automagically created class that holds the closure, while
total is a local. That should mean that each time around the loop, the
'list' field of this instance is being loaded twice. So, two field
accesses and a local access.
});

Benchmark("Using foreach: ", iterations, delegate
{
int total = 0;
foreach (int x in list)
total += x;
This will get a nice valuetype IEnumerator<int> implementation from the
list up front, so each time around the loop two methods are called
(MoveNext() and get_Current()) which can be inlined (because valuetypes
can't be subclassed), along with a local access. It turns out to take
70% longer than the next slowest though - interesting!
});

Benchmark("Using ForEach: ", iterations, delegate
{
int total = 0;
list.ForEach(delegate(int x) { total += x; });
In the previous two loops, total is a local, while in this one it's a
captured variable and thus is an instance field on a generated class for
the closure. That means it's actually got a speed-hit built-in that the
others don't have - yet it still turns out to be faster than the other
two.

One can infer from that that if you're doing something where you can
pack all your work into the closure itself, List<T>::ForEach has the
potential to be slightly faster again than for/index or foreach.
});
}
}
--->8---


-- Barry
May 10 '06 #7
Cool. This is how you might do it in c#:

List<string> l = new List<string>(File.ReadAllLines(@"c:\myfile.txt"));

int line = 0;

l.ForEach(delegate(string s) {

line++;

Console.WriteLine("{0}: {1}", line, s);

});
--
William Stacey [MVP]

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message news:MP************************@msnews.microsoft.c om...
| Joanna Carter [TeamB] <jo****@not.for.spam> wrote:
| > | See Jon Skeet explanation about Captured Variables here:
| > | <http://www.yoda.arachsys.com/csharp/csharp2/delegates.html>
| >
| > Fascinating stuff!! I *think* I understand it but, can someone give me a
| > real world but simple example of a useful use for this "feature" :-)
|
| I haven't used C# 2.0 much in anger yet, but Groovy has a very similar
| notion with closures. Because the closures don't need a separate
| definition (unlike delegates) you can use them much more widely without
| introducing a load of extra types. (This comes at the cost of compile-
| time safety, admittedly.)
|
| This makes it really easy to do a lot of things. For instance, here's a
| sample Groovy script to print out a file and its line numbers:
|
| line=0
| new File ("test.groovy").eachLine {
| line++
| println "$line: $it"
| }
|
|
| The "using" statement in C# is effectively a very specific type of
| closure - Groovy applies the concept very widely. C#/.NET 2.0 allows
| similar things to let you do something which each element in a list
| etc, but it's not quite as straight-forward as in Groovy.
|
| --
| Jon Skeet - <sk***@pobox.com>
| http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
| If replying to the group, please do not mail me too
May 10 '06 #8
"Joanna Carter [TeamB]" <jo****@not.for.spam> wrote:
Fascinating stuff!! I *think* I understand it but, can someone give me a
real world but simple example of a useful use for this "feature" :-)


In addition to the other comments, I'd just add that delegates have
been around in other languages for a while -- like Pascal and
Modula-3. They're also a natural part of the lambda-calculus, on which
much of computer science theory is based.

If you allow the ability to define nested procedures -- i.e. to define
procedures within other procedures -- then the variable lifetime stuff
we see in C# is inevitable.

--
Lucian
May 10 '06 #9
"Lucian Wischik" <lu***@wischik.com> a écrit dans le message de news:
k4********************************@4ax.com...

| In addition to the other comments, I'd just add that delegates have
| been around in other languages for a while -- like Pascal and
| Modula-3. They're also a natural part of the lambda-calculus, on which
| much of computer science theory is based.
|
| If you allow the ability to define nested procedures -- i.e. to define
| procedures within other procedures -- then the variable lifetime stuff
| we see in C# is inevitable.

Thenks to you and others for replies about the closures or delegates but,
,coming from a Delphi background we have been used to passing procedures and
nested procedures.

What I am really interested in finding out is this lifetime stuff and a
real-world use for the way the variables change/persist inside the
delegates. It looks both cool and wierd at the same time. Lots of things
like this can be solutions looking for a problem, so my real question is :
what is the problem that it can solve ?

:-))

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
May 10 '06 #10
"Joanna Carter [TeamB]" <jo****@not.for.spam> wrote:
It looks both cool and wierd at the same time. Lots of things
like this can be solutions looking for a problem, so my real question is :
what is the problem that it can solve ?


My take: it can't solve anything. Everything you could do with
anonymous delegates, you could instead do by creating a top-level
procedure and passing around a load of extra arguments to it. Or by
creating a class with a single "Invoke()" method on it. Anonymous
delegates are simply an easier way to write some of these things.

Most of the time they don't feel weird. If ever they do start to feel
weird, then your code probably won't be maintainable to other people
and you should stop!

--
Lucian
May 10 '06 #11
William Stacey [MVP] <wi************@gmail.com> wrote:
Cool. This is how you might do it in c#:

List<string> l = new List<string>(File.ReadAllLines(@"c:\myfile.txt"));

int line = 0;

l.ForEach(delegate(string s) {
line++;
Console.WriteLine("{0}: {1}", line, s);
});


Except that that requires the whole file to be read to start with,
unfortunately :( It's also a bit more work even just due to the
delegate (string s) bit. It's a lot nicer than alternatives in both C#
and "normal" Java (anonymous inner classes - blech) but it doesn't
encourage this style of programming to become widespread. In Groovy
it's rife :)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
May 10 '06 #12
Lucian Wischik <lu***@wischik.com> wrote:
"Joanna Carter [TeamB]" <jo****@not.for.spam> wrote:
It looks both cool and wierd at the same time. Lots of things
like this can be solutions looking for a problem, so my real question is :
what is the problem that it can solve ?


My take: it can't solve anything. Everything you could do with
anonymous delegates, you could instead do by creating a top-level
procedure and passing around a load of extra arguments to it. Or by
creating a class with a single "Invoke()" method on it. Anonymous
delegates are simply an easier way to write some of these things.

Most of the time they don't feel weird. If ever they do start to feel
weird, then your code probably won't be maintainable to other people
and you should stop!


I think they feel *particularly* weird when you have multiple closures
capturing different "versions" of a particular variable. It gets even
weirder when those closures may capture all the same version of a
different variable.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
May 10 '06 #13
Jon Skeet [C# MVP] <sk***@pobox.com> wrote:
[...] It's also a bit more work even just due to the
delegate (string s) bit.


Iterating through a List<T> using a delegate passed to ForEach is
actually faster.

-- Barry
May 10 '06 #14
Lucian Wischik <lu***@wischik.com> wrote:
My take: it can't solve anything. Everything you could do with
anonymous delegates, you could instead do by creating a top-level
procedure and passing around a load of extra arguments to it.
Or by creating a class with a single "Invoke()" method on it.
Of course, delegates are simply classes with a single Invoke() method.
Anonymous
delegates are simply an easier way to write some of these things.


They provide an extra abstraction tool - a way to easily pass arbitrary
code to other methods as if it were data.

For example, anonymous delegates can provide a way to make the Strategy
pattern easier to implement for cases where one might not consider it
because it might be too tedious. The Benchmark "algorithm" in my example
above is an example of a simple Strategy approach. The Benchmark
algorithm of timing an iterated loop over a block of code is
encapsulated in one place, and the code to execute is passed in. The
advantage of this over other techniques such as creating a separate
class is that it's more simple (when you understand the abstraction).

Just like object-oriented decomposition of a problem domain is a tool
that can make programs simpler when you understand how it works, passing
code blocks around can also make programs simpler.

-- Barry
May 10 '06 #15
| Except that that requires the whole file to be read to start with,
| unfortunately

True. But as the example ends up reading the whole file anyway, not sure it
matters all that much unless the file is large.
Not sure if the groovy "eachline" internally reads the whole thing into a
list first or not. I suppose File could implement an iterator on text files
to do the same kind of thing. Something like:

foreach(string s in File.ForEach("c:\my.txt"))
{
Console.WriteLine(s);
}
May 10 '06 #16
William Stacey [MVP] <wi************@gmail.com> wrote:
| Except that that requires the whole file to be read to start with,
| unfortunately

True. But as the example ends up reading the whole file anyway, not sure it
matters all that much unless the file is large.
Not sure if the groovy "eachline" internally reads the whole thing into a
list first or not.
I would be absolutely gobsmacked if it did. That would defeat most of
the point.
I suppose File could implement an iterator on text files
to do the same kind of thing. Something like:

foreach(string s in File.ForEach("c:\my.txt"))
{
Console.WriteLine(s);
}


Yes, that would indeed achieve the same kind of thing for this case.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
May 10 '06 #17
Barry Kelly <ba***********@gmail.com> wrote:
Jon Skeet [C# MVP] <sk***@pobox.com> wrote:
[...] It's also a bit more work even just due to the
delegate (string s) bit.


Iterating through a List<T> using a delegate passed to ForEach is
actually faster.


I meant work in terms of code. It doesn't look as neat.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
May 10 '06 #18

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

99
5818
by: David MacQuigg | last post by:
I'm not getting any feedback on the most important benefit in my proposed "Ideas for Python 3" thread - the unification of methods and functions. Perhaps it was buried among too many other less...
51
6881
by: Noam Raphael | last post by:
Hello, I thought about a new Python feature. Please tell me what you think about it. Say you want to write a base class with some unimplemented methods, that subclasses must implement (or...
19
2114
by: Skybuck Flying | last post by:
Hi, I think I might have just invented the variable bit cpu :) It works simply like this: Each "data bit" has a "meta data bit". The meta data bit describes if the bit is the ending bit...
7
3123
by: Edward Yang | last post by:
A few days ago I started a thread "I think C# is forcing us to write more (redundant) code" and got many replies (more than what I had expected). But after reading all the replies I think my...
12
5505
by: Andrew Poulos | last post by:
With the following code I can't understand why this.num keeps incrementing each time I create a new instance of Foo. For each instance I'm expecting this.num to alert as 1 but keeps incrementing. ...
2
1648
by: Samuel Siren | last post by:
Have just read about LINQ ant the new planned features of C# 3.0. I think it's fantastic, but I have a few questions about extension methods: Question 1: Syntax ------------------ Why is the...
3
1634
by: Deckarep | last post by:
I recently got into somewhat of a debate with a fellow co-worker. First I asked him if he new of a way to add a method to an existing class like enhancing a class but without using Inheritance. He...
4
3345
by: Steffen Bobek | last post by:
Extension methods are made for use with instances. I'd like to "misuse" them as static methods, too. Let me tell you my ambition: I use an extension method to serialize objects somehow like this:...
4
3295
by: Rene | last post by:
Hi, I was wondering if anyone could tell me why extension methods must be declared on static classes. I mean, why not allowing them to be declared as static methods in regular instance classes?...
0
7313
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7372
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
7029
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
7481
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
5619
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
1
5039
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
3190
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
3179
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
0
1537
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.