65

我对 lambda 变量的范围感到困惑,例如以下

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
    };

在第 1 行中,我声明了一个 lambda 变量 'o',这意味着我不能在第 2 行中再次声明它(或者如果我尝试这样做,至少编译器会抱怨)但是即使 'o1' 已经存在,它也不会抱怨第 3 行??

lambda 变量的作用域是什么?

4

8 回答 8

174

括号给出了线索​​ - lambda 变量在它声明的范围内被捕获:

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

请注意,这两个变量没有重叠o1,但它们都重叠(或隐藏)o变量,因此不能使用相同的名称。

于 2012-05-08T07:07:02.963 回答
14

lambda 参数的范围等于 lambda 表达式主体的整个范围,包括任何内部 lambda 表达式或范围。

如果我们扩展您的 lambda 表达式的语法并添加一些友好的缩进,它可能会变得更清晰(尽管可能没有yamen 的图解答案那么清晰!):

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

请注意,您的.Where().Max()呼叫位于外部.Where(). 外部 lambda 中的oin 被内部 lambda 中的外部 lambda 封装(这称为闭包),因此它已经存在于内部 lambda 的范围内,并且不能作为参数重用。

您可以重用o1,因为您的两个内部 lambda 彼此完全分开,因此它不会超出任何一个的范围。

于 2012-05-08T07:03:23.117 回答
5

如果一个作用域包含另一个作用域,则不能在两个作用域中使用相同的变量名。

在您的问题中,o是在外部范围中引入的,因此它不能在第二个Where()或 in 中再次使用Max(),因为这些范围包含在外部范围中。

另一方面,您可以o1在两个内部范围中使用,因为一个不包含另一个,所以那里没有歧义。

于 2012-05-08T07:05:45.703 回答
4

因为 lamda 是你代码中匿名函数的替换

相同范围

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

是变量“o”所在的函数范围

在第三行,这是变量的新片段,即新函数范围

不同的范围

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

所以这是第1行和第2行中的共振,第1行中定义的变量“o”不能在第2行中定义,因为相同的scrop和第2行中定义的“o1”可以在第3行中再次定义,因为它在不同的函数范围内

于 2012-05-08T07:03:05.093 回答
2

C# 不支持这样的阴影。

原因o1再次起作用,是它不会影响以前的o1.

于 2012-05-08T07:03:59.900 回答
2

它与任何其他变量相同。的范围o是第一个中的整个表达式Where,因此您不能在第二个中再次使用它,第二个在第一个中。但是范围o1只是你的第二个中的表达式Where,所以你可以在你的第二个Max之外的表达式中使用它Where。在代码中:

// 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
于 2012-05-08T07:07:34.890 回答
2

我试着按照你的代码把它画成这样......

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

相当粗略的解释方式,但你的括号决定了范围。你的第二个 o1 没有嵌套在第二个里面,否则你会遇到同样的问题

//outermost where

((BEGIN-o

//inner where

(BEGIN-o1 END-o1)

//max

(BEGIN-o1 END-o1)

END-o))
于 2012-05-08T07:14:16.447 回答
1
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

编译代码时,Action 中声明的 void 中的所有逻辑 ()=>{ ... }都将移动到名称错误的类型上的方法。

当新创建的函数到达堆栈上的那个位置时,运行时将调用它。

您可以通过各种方式将值传递到范围/ lambda,这对于将它们取出是相同的。

在 lambda 中声明的变量不能以其声明的名称在外部访问。

也可以利用反射来提取损坏的名称,但我不确定您是否需要。(如果我错了,请告诉我。)

于 2012-05-08T12:56:29.077 回答