C#中lambda变量的范围是什么?

时间:2012-05-08 07:00:35

标签: c# lambda scope

我对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变量的范围是什么?

8 个答案:

答案 0 :(得分:171)

括号提供线索 - lambda变量在声明的范围内捕获:

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

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

答案 1 :(得分:14)

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

如果我们扩展lambda表达式的语法并添加一些友好的缩进,它可能会变得更清晰(尽管可能没有yamen's diagrammatic answer那么明确!):

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

请注意,您的.Where().Max()来电位于外.Where()内。外部lambda中的o由内部lambda中的外部lambda封装(这称为 closure ),因此它已经存在于内部lambda的范围内,并且不能重用作为参数。

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

答案 2 :(得分:5)

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

在您的问题中,o是在外部范围内引入的,因此无法在第二个Where()Max()中再次使用,因为这些范围包含在外部范围内

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

答案 3 :(得分:4)

因为lamda在你的代码中取代了匿名函数

相同范围

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

是变量“o”live

的函数范围

这里是第三行,这是变量的新scrop,即新函数范围

不同范围

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

因此,line1和line2中的共振变量line1中的变量“o”在line2中无法定义,因为相同的scrop和line2中的“o1”定义可以在line3中再次定义,因为它处于不同的函数范围

答案 4 :(得分:2)

C#不支持这样的阴影。

o1再次发挥作用的原因是,它不会影响之前的o1

答案 5 :(得分: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

答案 6 :(得分: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没有嵌套在第二个o1中,否则你会遇到同样的问题

//outermost where

((BEGIN-o

//inner where

(BEGIN-o1 END-o1)

//max

(BEGIN-o1 END-o1)

END-o))

答案 7 :(得分: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中声明的变量无法使用声明的名称在外部访问。

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