无论是expression lambda还是statement lambda,都适用以下的描述:
Lambda Expressions - This is a shorthand for specifying a method. The C# compiler will translate each into either an anonymous method or a true System.Linq.Expressions.Expression. You really need to understand these to use Linq well. There are three parts: A parameter list, an arrow, and a method body.
1.匿名方法的例子:(匿名方法可以作为delegate直接传给接受Action<>,Func<>这些concrete delegate的地方,也就是编译器可以自动转换,但如果接受方需要的是Delegate这个抽象的类对象,比如Dispatch.Invoke方法,就要类型转换,这里有讨论这个问题的。)
new Action(() =>{...})
lambda表达式变成了 .method public hidebysig instance void <LoadData>b__8() cil managed
<>c__DisplayClassa CS$<>8__localsb; (这里Reflector翻译的C#不准,看IL指令集,实际是newobj指令,把当前context创建出来)
CS$<>9__CachedAnonymousMethodDelegate9 = new Action(CS$<>8__localsb, (IntPtr) this.<LoadData>b__8);
(调用了Lambda表达式形成的匿名方法)
2.转成Expressions的例子,生成了暴多的IL,应用了很多Expressons命名空间的类和方法:
h => h.CompanyCode == "1" 会变成 Expression<Func<T, bool>> expr
: ldtoken [CdcSoftware.Erp.RuntimeServices]CdcSoftware.Erp.RuntimeServices.Entities.Finance.SalesOrderHeader
L_0012: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0017: ldstr "h"
L_001c: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
L_0021: stloc.3
L_0022: ldloc.3
L_0023: ldtoken instance string [CdcSoftware.Erp.RuntimeServices]CdcSoftware.Erp.RuntimeServices.Entities.Finance.SalesOrderHeader::get_CompanyCode()
L_0028: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_002d: castclass [mscorlib]System.Reflection.MethodInfo
L_0032: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
L_0037: ldstr "1"
L_003c: ldtoken string
L_0041: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
L_0046: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
L_004b: ldc.i4.0
L_004c: ldtoken bool [mscorlib]System.String::op_Equality(string, string)
L_0051: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
L_0056: castclass [mscorlib]System.Reflection.MethodInfo
L_005b: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression, bool, class [mscorlib]System.Reflection.MethodInfo) //这时栈内是第一个参数,lambda的body Expression
L_0060: ldc.i4.1
L_0061: newarr [System.Core]System.Linq.Expressions.ParameterExpression
L_0066: stloc.s CS$0$0002
L_0068: ldloc.s CS$0$0002
L_006a: ldc.i4.0
L_006b: ldloc.3
L_006c: stelem.ref
L_006d: ldloc.s CS$0$0002 //把ParameterExpression[0]加载到栈里(第二个参数,lambda的左边的参数集合)
L_006f: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class [CdcSoftware.Erp.RuntimeServices]CdcSoftware.Erp.RuntimeServices.Entities.Finance.SalesOrderHeader, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
题外话:关于IL的参数顺序,生成的IL都是从左到右压到栈里的,但为什么网上都说c#是stdcall呢(也是从右到左的)。这里指出:“当我们使用 call 等指令调用一个方法时,CLR 为目标方法在调用堆栈上新分配一个堆栈帧,并将方法参数从当前方法的计算堆栈弹出压入目标方法的参数表中,接着执行流程跳转到目标方法。”注意弹出压入一词,在老方法里压栈的顺序是param1,param2,则老栈内是param2在param1上面 弹出压入新参数表栈后,由于是param2先弹压,就变成了param2在param1下面了。这就是从右到左的过程。