5

Consider this code snippet and try to guess what y1 and y2 evaluate to

static class Extensions
{
    public static Func<T> AsDelegate<T>(this T value)
    {
        return () => value;
    }
}
class Program
{
    static void Main(string[] args)
    {
        new Program();
    }

    Program()
    {
        double x = Math.PI;

        Func<double> ff = x.AsDelegate();
        Func<double> fg = () => x;

        x = -Math.PI;

        double y1 = ff();  // y1 = 3.141..
        double y2 = fg();  // y2 = -3.141..

    }
}

You might say -Aha- double is a value type and so the value returned by the extension method is a copy of the main x. But when you change the above into delegates of classes the results are still different. Example:

class Foo
{
    public double x;
}
    Program()
    {
        Foo foo = new Foo() { x=1.0 };

        Func<Foo> ff = foo.AsDelegate();
        Func<Foo> fg = () => foo;

        foo = new Foo() { x = -1.0 };

        double y1 = ff().x;    // y1 = 1.0
        double y2 = fg().x;    // y2 = -1.0
    }

So the two functions must return two different instances of the same class. It is interesting to consider that ff() carries with it a reference to local variable foo, but fg() does not and it relies on what is in scope currently.

So what happens when these two delegates are passed on to other parts of the code which do not have visibility to foo instance? Somehow the question of who owns what information (data) is becoming less and less clear when extension methods are combined with delegates.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133

7 Answers7

7

AsDelegate captures the variable value (the parameter of AsDelegate), while () => x captures the variable x. So if you change the value of x, the lambda expression will return a different value. Changing x doesn't change value though.

See: Outer Variable Trap

Community
  • 1
  • 1
dtb
  • 213,145
  • 36
  • 401
  • 431
  • Strictly speaking it captures the *argument* value; the value of which is initially the value of x, and never gets changed. A subtle distinction... – Marc Gravell Nov 09 '10 at 18:07
4

In AsDelegate, we are capturing the the argument "value". the value of this argement is taken as a copy of the value of the variable at the time the method is invoked, and never changes - so we see the original object.

The direct lambda captures the variable foo (not the value of the variable - the variable itself) - thus we do see changes to this.

Basically adding a method call changed the thigh being captured.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

The AsDelegate extension method uses the value of x at the time AsDelegate is called whereas the lambda expression () => x captures the variable x and therefore the value of x is taken at the time the expression is invoked (rather than the value when it was defined).

Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
3

ff captures (binds) to the value of x at this line:

Func<double> ff = x.AsDelegate();

By contrast, fg binds to the variable x at this line:

Func<double> fg = () => x;

So, when the value of x changes, ff is unafected, but fg changes.

Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
Bevan
  • 43,618
  • 10
  • 81
  • 133
  • Meh... It a: doesn't bind to a *value* of anything (except the compiler-generates capture class instance), and b: doesn't strictly bind to "x" *at all* - clearer yes, but I think perhaps a bit misleading due to that simplicity. (simplicity is good though) – Marc Gravell Nov 09 '10 at 18:39
  • @Marc - it's a toss up. I figured to focus on the way it appears to a user, rather than diving into the way it's implemented. – Bevan Nov 09 '10 at 20:28
  • 1
    granted. Actually, the main point of my comment was for the "long tail" - so that someone seeing the answer can see (without scrolling down) that there is a little more to it. Yours *is* a good answer ;p – Marc Gravell Nov 09 '10 at 20:38
2

() => x captures x value. Special class is created by compiler to handle lambdas or anonymous delegates, any variable used in the lambda is captured.

For example if you run following code:

    List<Func<int>> list = new List<Func<int>>();

    for (int i = 0; i < 5; i++)
    {
        list.Add(() => i);
    }

    list.ForEach(function => Console.WriteLine(function()));

you will see that numbers printed are the same.

Andrew Bezzub
  • 15,744
  • 7
  • 51
  • 73
  • So when function() is called it uses whatever value i has at that time (outside of the loop)? I thought that the scope of `i` ends when the loop ends... – John Alexiou Nov 09 '10 at 18:14
2

You need to read about closures in C#. See also Wikipedia.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
jason
  • 236,483
  • 35
  • 423
  • 525
0

The first method creates a new delegate to a given function and stores it. It you later overwrite foo, your newly created delegate is not touched.

The second one is a lambda expression which lifts/*captures* its context, that means the foo variable. All changes to variables that has been lifted by lambdas expressions are seen by that lambda expression.

codymanix
  • 28,510
  • 21
  • 92
  • 151