admin管理员组

文章数量:1389681

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

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

Share Improve this question edited May 7, 2011 at 21:11 Pacerier asked May 7, 2011 at 20:24 PacerierPacerier 89.9k111 gold badges385 silver badges645 bronze badges 8
  • Are you talking about issues as in performance, or...? – Matti Virkkunen Commented May 7, 2011 at 20:30
  • code example, please. Also, what do you mean by, "it is beneficial"? what are the benefits you are referring to? – Cheeso Commented May 7, 2011 at 20:34
  • @Matti Virkkunen i've edited the question – Pacerier Commented May 7, 2011 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 Commented May 7, 2011 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 Commented May 7, 2011 at 21:50
 |  Show 3 more ments

3 Answers 3

Reset to default 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 piler-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 piler 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.

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 piler 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 piler 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).

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.

本文标签: does C and VB lambdas have **scope chain** issues similar to javascriptStack Overflow