什么是在C#lambdavariables的范围?

我对lambdavariables的范围感到困惑,比如下面的例子

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

在第一行中,我已经声明了一个lambdavariables“o”,这意味着我不能在第二行再次声明它(或者至less编译器会抱怨,如果我尝试的话)但是即使'o1'已经存在,它也不会抱怨第3行??

什么是lambdavariables的范围?

括号给出了线索​​ – lambdavariables被捕获在它声明的范围内:

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

请注意,两个o1variables没有重叠,但它们都重叠(或遮蔽) ovariables,因此不能使用相同的名称。

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

如果我们扩展你的lambdaexpression式的语法并添加一些友好的缩进,它可能会变得更清晰(虽然可能没有衙门的图解答案那么清晰):

 .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封装(这称为闭包 ),因此它已经存在于你的内部lambda中,并且不能作为参数重用。

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

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

在你的问题中, o被引入了外部范围,所以它不能在第二个Where()Max()再次使用,因为这些范围包含在外部范围中。

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

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

同样的范围

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

是可变的“o”生活的function的范围

这里在第三行这是variables,即新function范围的新scrop

不同的范围

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

因此line1和line2中的共振由于相同的scrop,line1中定义的varialbe“o”不能在line2中定义,而line2中的“o1”定义可以再次定义在line3中,因为它处于不同的function范围

C#不支持这样的阴影。

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

这与任何其他variables相同。 o的范围在你的第一个Where是整个expression式,所以你不能在第二个地方再次使用它。 但是o1的范围只是在第二个Where的expression式中,所以你可以在Max的expression式中使用它,这是在第二个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 

我按照你的代码尝试像这样

 .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)) 
 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的所有逻辑将被移动到具有重名的types的方法上。

运行时间将在新build函数到达堆栈时调用。

你可以通过不同的方式将值传递给一个范围/ lambda,这与获取它们是一样的。

在lambda中声明的variables不能在外部使用声明的名称访问。

也可以利用reflection来拉出重名的名字,但我不确定你是否需要这样做。 (如果我错了,请告诉我。)