我对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变量的范围是什么?
答案 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中声明的变量无法使用声明的名称在外部访问。
也可以利用反射来提取受损的名称,但我不确定你是否需要。 (如果我错了,请告诉我。)