4

I've read that due to how the scope chain works in javascript, if we wish to refer to a variable V within a function F that is not declared within the F's scope, it is beneficial (yes in terms of performance) to declare a local variable V2 in F that references V, then accessing the object referenced by V through V2.

i'm wondering if this concept applies to the closures in C# and VB (accessing local variables in functions through lambdas)

Public Shared Function Example()
    Dim a = 1
    Dim b = New Object
    Return Sub()
               'when we use the variables a and b from here does it have to "go up the scope chain"
           End Sub
End Function

btw i would prefer if the answer isn't premature optimization is the root of all evil

Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • Are you talking about issues as in performance, or...? – Matti Virkkunen May 07 '11 at 20:30
  • code example, please. Also, what do you mean by, "it is beneficial"? what are the benefits you are referring to? – Cheeso May 07 '11 at 20:34
  • @Matti Virkkunen i've edited the question – Pacerier May 07 '11 at 21:11
  • 1
    now that you've updated your question: i have no idea, but it should be easy for you to test. – Cheeso May 07 '11 at 21:15
  • The scope chain in JavaScript is a specification abstraction. In EcmaScript 5 strict mode, where `with` is not allowed, and `eval("var x;")` cannot add bindings to the local environment, and in regular EcmaScript functions that do not use `with` or `eval`, there is no need for an interpreter to walk the scope chain to find a local variable binding. It was only the (perceived) need to handle aliased `eval` that caused this inefficiency in older interpreters. – Mike Samuel May 07 '11 at 21:50
  • @Mike Samuel, I'd like to learn more about EcmaScript5, its adoption and support in various environments. Do you have any suggestions on that? Maybe I should ask a new question. – Cheeso May 07 '11 at 22:36
  • @Cheeso, There's lots of existing questions you can browse : http://stackoverflow.com/questions/tagged/ecmascript-5 and http://stackoverflow.com/questions/2280115/ecmascript-5-browser-implementation directly addresses your first concern. – Mike Samuel May 07 '11 at 23:14
  • @MikeSamuel: Even when not using `with`, suppose (in the loosy-goosy non-strict dialect) that function1 defines a variable `foo` and creates function2; function2 creates and calls function3 without defining its own `foo`, then defines `foo` and calls function3 again. If function3 writes (but does not define) `foo`, my understanding is that the first call would write function1's `foo` and the second would write function2's `foo`; I see no way to achieve such behavior without walking scope chains. Why would `with` or `eval` be necessary to see such behavior? – supercat Aug 28 '14 at 16:33

3 Answers3

7

Short answer: no. .NET doesn't need to walk up the scope chain to find the variables.

Long answer:

Start with this example:

static Func<string> CaptureArgs(int a, int b)
{
    return () => String.Format("a = {0}, b = {1}", a, b);
}

static void Main(string[] args)
{
    Func<string> f = CaptureArgs(5, 10);
    Console.WriteLine("f(): {0}", f());
    // prints f(): a = 5, b = 10
}

In the CaptureArgs method, a and b exist on the stack. Intuitively, if we reference the variables in an anonymous function, return the function and popping the stack frame should remove a and b from memory. (This is called the upward funargs problem).

C# doesn't suffer from the upwards funargs problem because, behind the scenes, an anonymous function is just fancy syntax sugar over a compiler-generated class. The C# code above turns into:

private sealed class <>c__DisplayClass1
{
    // Fields
    public int a;
    public int b;

    // Methods
    public string <CaptureArgs>b__0()
    {
        return string.Format("a = {0}, b = {1}", this.a, this.b);
    }
}

The compiler creates and returns a new instance of <>c__DisplayClass1, initializes its a and b fields from the a and b passed into the CaptureArgs method (this effectively copies a and b from the stack to fields existing on the heap), and returns it to the caller. Calling f() is really a call to <>c__DisplayClass1.<CaptureArgs>b__0().

Since the a and b referenced in <CaptureArgs>b__0 are vanilla fields, they can be referenced directly by the delegate, they don't require any special sort of scope chaining rules.

Juliet
  • 80,494
  • 45
  • 196
  • 228
  • does that mean that everytime a lambda is created a new class is generated? is it discouraged to make extensive use of lambdas in a C#/VB program? – Pacerier May 08 '11 at 00:48
6

If I understand it correctly, the problem with JavaScript is following: When you access a variable in a (deeply) nested scope, the runtime needs to walk through all parent scopes to locate the variable.

Lambdas in C# or Visual Basic do not suffer from this issue.

In VB or C#, the compiler knows exactly which variable are you referring to in a lambda function, so it can create a direct reference to the variable. The only difference is that captured variables (those accessed from nested scope) have to be turned from a local variable into a field (in some object, also called a closure).

To add an example - say you write something (crazy) like this:

Func<Func<int>> Foo() {
  int x = 10;
  return () => {
    x++;
    return () => x;
  }
}

This is a bit silly, but it demonstrates nested scoping. The variable is declared in one scope, set in a nested scope and read in an even deeper scope. The compiler will produce something like this:

class Closure { 
  public Closure(int x) { this.x = x; }
  public int x;  
  public Func<int> Nested1() { 
    x++;
    return Func<int>(Nested2);
  }
  public int Nested2() { return x; }
}

Func<Func<int>> Foo() {
  var clo = new Closure(10);
  return Func<Func<int>>(clo.Nested1);
}

As you can see - there is not walking through a chain of scopes. Each time you access the variable x, the runtime directly accesses some location in memory (allocated on the heap, instead of a stack).

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • does that mean that everytime a lambda is created a new class is generated? is it discouraged to make extensive use of lambdas in a C#/VB program? – Pacerier May 08 '11 at 00:27
  • 1
    I believe that the compiler groups some lambdas into a single class. This is definitely not a reason for avoiding lambdas - compiled class doesn't add too much to the program size and you would have to write a lot more code without lambdas. – Tomas Petricek May 08 '11 at 03:40
3

Short answer: No.

C# closures are implemented in a static fashion (closed over variables are explicitly known and bound) and does not traverse through a [[scope chain]], as in Javascript.

Run some tests to put your mind at ease, then just don't worry about it ;-)

Happy coding.