什么是在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
请注意,两个o1
variables没有重叠,但它们都重叠(或遮蔽) o
variables,因此不能使用相同的名称。
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来拉出重名的名字,但我不确定你是否需要这样做。 (如果我错了,请告诉我。)