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

C#中lambda变量的范围是什么?,c#,lambda,scope,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)

我对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变量的作用域是什么?

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

相同范围

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
变量“o”所在的函数范围

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

不同的范围

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

因此,这是第1行和第2行变量中的原因。第1行中定义的“o”无法在第2行中定义,因为第2行中定义的“o”可以在第3行中再次定义,因为它位于不同的函数范围中。lambda参数的范围等于lambda表达式主体的整个范围,包括任何内部lambda表达式或作用域

如果我们扩展lambda表达式的语法并添加一些友好的缩进,它可能会变得更清晰(尽管可能没有比这更清晰的地方了!)

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

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

C#不支持这样的阴影


之所以
o1
能再次发挥作用,是因为它不会对前一个
o1

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

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


另一方面,您可以在两个内部作用域中使用
o1
,因为其中一个不包含另一个,因此不存在歧义。

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

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

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

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

// 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

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

.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))
编译代码时,操作
()=>{…}
中声明的void中的所有逻辑都将移动到名称已损坏的类型上的方法

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

您可以通过各种方式将值传递到scope/lambda中,这与将值传递出去的方式相同

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


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

您有下面的答案,我只想分享一个简单实用的提示:当读代码时范围不清楚,并且范围很重要时,或者使用不同的变量名来避免视觉上的歧义;或者用大括号写下你的lambda,让它们看起来更像函数——当你看到传统的花括号时,大多数程序员的思维和眼睛会做出更清晰的区分。这是我3年来看到的最好的答案。@kizzx2你应该经常来这里:)这是一种非常优雅的方式来描绘ScopeOther+1,这是一种很棒的可视化效果!我的回答更简单易读,更一般地回答这个问题。其他的不显示作用域或者不能在作用域中重复名称,例如(o=>o.Somethings.Where(o=>o.IsTrue))//错误,因为o已经在使用中。我应该得到更多的名声。