C#中lambda变量的范围是什么?
我对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)
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已经在使用中。我应该得到更多的名声。