lambda usages

P

puzzlecracker

I am not understanding what's happening in the code below (from
accelerated c#) -- I know what the outcome but not how .net does it:

using System;
using System.Linq;


public class LambdaTest
{
static void Main()
{
int counter = 0;
WriteStream( () => counter++ );
Console.WriteLine( "Final value of counter: {0}", counter );
}
static void WriteStream( Func<int> counter )
{
for( int i = 0; i < 10; ++i )
{
Console.Write( "{0}, ", counter() );
}
Console.WriteLine();
}
}

From what I understand, we are creating Func generic delegate ,
which is takes no parameters and returns the int. We and to that
delegate we pass local value type counter. Then why would counter
changed upon the retrun from WriteStream? Doesn't WriteSteam operate
on the local counter argument?

need some lambda education....

Thanks
 
M

Mythran

puzzlecracker said:
I am not understanding what's happening in the code below (from
accelerated c#) -- I know what the outcome but not how .net does it:

using System;
using System.Linq;


public class LambdaTest
{
static void Main()
{
int counter = 0;
WriteStream( () => counter++ );
Console.WriteLine( "Final value of counter: {0}", counter );
}
static void WriteStream( Func<int> counter )
{
for( int i = 0; i < 10; ++i )
{
Console.Write( "{0}, ", counter() );
}
Console.WriteLine();
}
}

From what I understand, we are creating Func generic delegate ,
which is takes no parameters and returns the int. We and to that
delegate we pass local value type counter. Then why would counter
changed upon the retrun from WriteStream? Doesn't WriteSteam operate
on the local counter argument?

need some lambda education....

Thanks

Well, we are not passing counter into the generic delegate, the generic
delegate is using the local counter (local to the outer function) which is
the same value at the same address when updated/modified from the Main
function or the delegate. The "equivalent" method would be:

static int Increment(ref int counter) { counter++; }

By equivalent I mean that counter is being "passed" into the method (by
reference) instead of the method accessing the variable of the parent code
block directly.

I believe this is correct but I'm not 100% so I hope someone else replies if
I'm wrong :)

HTH,
Mythran
 
B

Ben Voigt [C++ MVP]

Mythran said:
Well, we are not passing counter into the generic delegate, the
generic delegate is using the local counter (local to the outer
function) which is the same value at the same address when
updated/modified from the Main function or the delegate. The
"equivalent" method would be:
static int Increment(ref int counter) { counter++; }

By equivalent I mean that counter is being "passed" into the method
(by reference) instead of the method accessing the variable of the
parent code block directly.

I believe this is correct but I'm not 100% so I hope someone else
replies if I'm wrong :)

Mostly right, but the difference is that here we have a closure, the
reference to the function's counter variable is part of the delegate. With
a reference argument the caller (i.e. WriteStream) would have to pass the
reference, here the caller isn't involved in any way.
 
P

puzzlecracker

Mostly right, but the difference is that here we have a closure, the
reference to the function's counter variable is part of the delegate.  With
a reference argument the caller (i.e. WriteStream) would have to pass the
reference, here the caller isn't involved in any way.

Does anyone know what how Func is defined and implemented?
 
P

puzzlecracker

This is a significant point. If the variable were simply being passed by
reference, then the lambda expression would be useless after Main() had
returned (ignoring for the moment that Main() is a special method that,
after it returns, terminates the process :) ). By capturing the variable,
the lambda expression remains viable even after the Main() method has
returned.

Is this really possible? I don't see how you can move life-time of
the local counter variable beyond it's enclosed space, which is main
our case. It's conceptually wrong to prolong a variables lifetime.

Actually, on the second thought: perhaps ones example can be a
returning the of lambada expression from a function which used its
local variable to initialize it...


Someone needs to demonstrate it with an example... any volunteers?
 
P

puzzlecracker

Func<T> is just a delegate type. You can declare it yourself if you like:

delegate T Func<T>();

It's not "implemented" at all. But you can create an instance of the type
in a variety of ways, all involving providing a method implementation that
matches the signature of the delegate type. In the case of anonymous
methods, the compiler can infer and cast to the correct type as long as
the anonymous method is compatible (i.e you haven't written it in a way
that is mutually exclusive with the desired type).

Pete

I tend to think of delegates as classes, hence perceived Func as a
functor...am a paltry and recovering C++ developer
 
B

Ben Voigt [C++ MVP]

Peter Duniho said:
Does anyone know what how Func is defined and implemented?

Func<T> is just a delegate type. You can declare it yourself if you
like:

delegate T Func<T>();

It's not "implemented" at all. [...]

I tend to think of delegates as classes, hence perceived Func as a
functor...am a paltry and recovering C++ developer

I'm not sure what you mean. A delegate type is a class and a delegate
instance can indeed be thought of as a functor.

So we might say a delegate type is an interface for functors, or even (dare
I?), a "concept".
 
B

Ben Voigt [C++ MVP]

Peter Duniho said:
Yes, of course. I wouldn't have written it otherwise.


It's definitely conceptually wrong. Except when it's right. :p


I thought the original example in this thread demonstrated it reasonably.
But taking your suggestion as a more overt version, here's what the code
would look like:

void MethodA()
{
Func<int> func = MethodB();

Console.WriteLine(func());
Console.WriteLine(func());
}

Func<int> MethodB()
{
int i = 5;

return () => i++;
}

Use Reflector on this. You'll find that what looks like a local variable no
longer is. The captured local variables in any scope are moved into a new
anonymous class, which the anonymous delegate (written with lambda syntax
but that doesn't matter) is a member of so it gets the full context.
Something like:


Func<int> MethodB()
{
class Closure_Uniquified
{
int i;
int AnonDelegate01() { return i++; }
}
Closure_Uniquified captured = new Closure_Uniquified();
captured.i = 5;
 

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