What is the scope of a lambda variable in C#?

2019-01-22 10:51发布

问题:

I'm confused about the scope of the lambda variable, take for instance the following

var query = 
    from customer in clist
    from order in olist
    .Where(o => o.CustomerID == customer.CustomerID && o.OrderDate ==  // line 1
        olist.Where(o1 => o1.CustomerID == customer.CustomerID)        // line 2
             .Max(o1 => o1.OrderDate)                                  // line 3
    )
    select new {
        customer.CustomerID,
        customer.Name,
        customer.Address,
        order.Product,
        order.OrderDate
    };

In line 1 I have declare a lambda variable 'o' which means I cannot declare it again in line 2 (or at least the compiler complains if I try to) But it doesn't complain about line 3 even though 'o1' already exists??

What is the scope of a lambda variable?

回答1:

The brackets give the clue - the lambda variable is captured in the scope of where it's declared:

.Where(o => ... olist.Where(o1 => ...).Max(o1 => ...))
  //  |----------------------------------------------| scope of o
  //                       |---------|                 scope of first o1
  //                                      |---------|  scope of second o1

Note that there's no overlap for the two o1 variables, but they both overlap (or shadow) the o variable and hence can't use the same name.



回答2:

The scope of a lambda parameter is equal to the entire scope of the body of the lambda expression, including any inner lambda expressions or scopes.

If we expand the syntax of your lambda expressions and add some friendly indentation it may become clearer (though probably nowhere as clear as yamen's diagrammatic answer!):

.Where(o => {
    return o.CustomerID == customer.CustomerID
        && o.OrderDate == olist.Where(
            o1 => o1.CustomerID == customer.CustomerID
        )
        .Max(
            o1 => o1.OrderDate
        );
})

Notice that your .Where().Max() call is located within an outer .Where(). The o in the outer lambda is encapsulated by the outer lambda within your inner lambdas (this is called closure) such that it exists in the scope of your inner lambdas already, and cannot be reused as a parameter.

You can reuse o1 because your two inner lambdas are completely separate from each other, so it doesn't live beyond the scope of either one.



回答3:

You cannot use the same variable name in two scopes if one of the scopes contains the other.

In your question, o is introduced in the outer scope, so it cannot be used again in the second Where() or in Max(), because these scopes are contained in the outer one.

On the other hand, you can use o1 in both inner scopes because one does not contain the other, so there is no ambiguity there.



回答4:

becasue lamda is replacement of the anonymous function here in you code

Same Scope

Where(o => o.CustomerID == customer.CustomerID && o.OrderDate ==   //line 1
         olist.Where(o1 => o1.CustomerID == customer.CustomerID)   //line 2     

is scope of function in which varialble "o" live

here in line three this is new scrop of the variable i.e new function scope

Different Scope

  .Max(o1 => o1.OrderDate)   )        //line 3

so that is the reson in line1 and line2 varialbe "o" defined in line1 cannot defined in line2 because of the same scrop and "o1" define in line2 can be defined again in line3 becauase it is in different function scope



回答5:

C# does not support shadowing like that.

The reason o1 works again, is that it does not shadow the previous o1.



回答6:

It is the same as with any other variable. The scope of o is the whole expression in your first Where, so you cannot use it again in the second, which is inside the first. But the scope of o1 is just the expression in your second Where, so you can use it in the expression of your Max, which is outside the second Where. In the code:

// o scope lasts until the first bracket is closed
Where(o => o.CustomerID == customer.CustomerID && o.OrderDate ==
// o1 scope lasts until the second bracket is closed; the first is not yet closed here
        olist.Where(o1 => o1.CustomerID == customer.CustomerID)
// The second bracket is closed, so o1 is already out of scope; o is still in scope
             .Max(o1 => o1.OrderDate)
)
// The first bracket is closed, so o is finally out of scope


回答7:

I try picture it like this as per your code...

.Where(o => o.CustomerID == customer.CustomerID && o.OrderDate ==  // line 1
        olist.Where(o1 => o1.CustomerID == customer.CustomerID)        // line 2
             .Max(o1 => o1.OrderDate)                                  // line 3
    )

Rather crude way of explaining but your brackets determine the scope. Your 2nd o1 isn't nested inside the second where, other wise you'd have the same problem

//outermost where

((BEGIN-o

//inner where

(BEGIN-o1 END-o1)

//max

(BEGIN-o1 END-o1)

END-o))


回答8:

var external = 1;

//This is a scope
Action scope = new Action(() =>
{
    //myVar is not accessible from outside
    var myVar = 0 + external;
    Console.WriteLine(myVar); //outputs 1
});

//Call the scope
scope();
//Console.WriteLine(myVar);//Will not compile

When the code is compiled, all the logic from the void declared in the Action ()=>{ ... } will be moved to a method on the type with a mangled name.

The run time will call the newly created function when it is reaches that place on the stack.

You can pass values into a scope / lambda in various ways which is the same for getting them out.

Variables declared in a lambda are not accessible outside with their declared name.

It is also possible to utilize reflection to pull out the mangled name however I am not sure you require that. (Please let me know if I am wrong.)



标签: c# lambda scope