继上一章,介绍Extension Method之后,我们接着来介绍另一个重要的特性:Lambda Expression。在前面的两篇文章中,我一再在强调这样的一个概念:C# 3.x新引入的这些特性仅仅反映在Programming Language和相应的Compiler层面。通过编译生成的Assembly的IL和原来并没有本质的改变。从这个意义上讲,所有的这些其实是编译器给我们玩得障眼法而已。Lambda Expression也不例外, Lambda Expression就是一个Anonymous Delegate,无论是Named Delegate也好、Anonymous Delegate也好,其本质也就是一个Delegate。
接下来,我将通过一个简单的Demonstration,由浅入深地分析Lambda Expression,看看编译器到底会编译生成怎样的额外的Code,他们的IL又是如何。
一、Named Delegate
在上面,我说了Lambda Expression本质上就是一个Delegate,我们先不直接来介绍Lambda Expression, 我们先来看看我们最为熟悉的Delegate的例子:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
Artech.LambdaExpression
{
classProgram

{
staticvoidMain()

{
_namedMethodDelegate=newFunction<int,bool>(SomeMethod);
Function<int,bool>function1=_namedMethodDelegate;
function1(20);
}
privatestaticFunction<int,bool>_namedMethodDelegate;
privatestaticboolSomeMethod(intargs)

{
return(args>0);
}
}
delegateTResultFunction<TArgs,TResult>(TArgsargs);
}
上面的例子很简单,先定一个Generic Delegate :Function。在Program Class中定义一个Static的Function字段_namedMethodDelegate和与之对应的Method:SomeMethod,判断输入的数字是否大于零。在Main中实例化_namedMethodDelegate,并调用它。
我们通过IL Disassembler这个Utility来看看Main方法的IL代码。为了让对IL Instruction不是很了解的读者更加容易地理解整个执行过程,我加了简单注释。对于那些希望进一步了解整个MSIL Instruction列表的读者,可以参考:MSIL Instruction Table。
.method
private
hidebysig
static
void
Main()cilmanaged
{
.entrypoint
//Codesize34(0x22)
.maxstack3
.localsinit([0]classArtech.LambdaExpression.Function`2<int32,bool>function1)//Initializefunction1
IL_0000:nop
IL_0001:ldnull
IL_0002:ldftnboolArtech.LambdaExpression.Program::SomeMethod(int32)//PushesthemethodpointerreferencedbySomeMethod.
IL_0008:newobjinstancevoidclassArtech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
nativeint)//InitializeraArtech.LambdaExpression.Functiondelegateinstance.
IL_000d:stsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::_namedMethodDelegate//Storesastaticfield:_namedMethodDelegate
IL_0012:ldsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::_namedMethodDelegate//Pushesthestaticfield(_namedMethodDelegate)ofanobjectStatic.
IL_0017:stloc.0//Popthefirstlocalvariable
IL_0018:ldloc.0//Pushesthefirstlocalvariable
IL_0019:ldc.i4.s20//Pushesspecified8-bitvalue(20)as32-bit
IL_001b:callvirtinstance!1classArtech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)//Callsvirtualmethodofdelegateinstance.
IL_0020:pop
IL_0021:ret
}
//
endofmethodProgram::Main
对于Delegate,我无须再作深入的介绍,相信大家早已了如指掌。在这里需要着重提出是,上面介绍的内容将是后续部分的基础,通过后面的对Anonymous Method和Lambda expression介绍,你会发现它们生成的代码结构和上面的是非常相似的。
二、Anonymous Method Delegate
Anonymous Method是C# 2.0引入的一个非常好用的功能。通过Anonymous Method,我们可以Delegate的实现直接以Inline的方式放入Delegate对象使用的位置,而无须再繁琐地创建一个Delegate,并通过定义在某个Class中具有相同申明的Method来事例化这个Delegate Instance,最后才将这个delegate instance传入需要调用的Method。
我们现在通过Anonymous Method来简化上面的代码。
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
Artech.LambdaExpression
{
classProgram

{
staticvoidMain()

{
Function<int,bool>function2=delegate(intargs)

{
returnargs>0;
};
function2(20);
}
}
delegateTResultFunction<TArgs,TResult>(TArgsargs);
}
我们通过Reflector分析编译生成的Assembly,我们发现它具有下面的结构。进一步分析Program Class,我们发现它多了两个额外的Static成员:<>9__CachedAnonymousMethodDelegate1和<Main>b__0。这是编译器的功劳。
下面分别是<>9__CachedAnonymousMethodDelegate1和<Main>b__0的定义:
[CompilerGenerated]
private
static
Function
<
int
,
bool
>
<>
9__CachedAnonymousMethodDelegate1;
[CompilerGenerated]
private
static
bool
<
Main
>
b__0(
int
args)
{
return(args>0);
}
是不是我我们上面一节定义的_namedMethodDelegate 和SomeMethod 这个两个静态成员一样?
我们进一步分析Main Method的IL。
.method
private
hidebysig
static
void
Main()cilmanaged
{
.entrypoint
//Codesize43(0x2b)
.maxstack3
.localsinit([0]classArtech.LambdaExpression.Function`2<int32,bool>function2)
IL_0000:nop
IL_0001:ldsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
IL_0006:brtrue.sIL_001b
IL_0008:ldnull
IL_0009:ldftnboolArtech.LambdaExpression.Program::'<Main>b__0'(int32)
IL_000f:newobjinstancevoidclassArtech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
nativeint)
IL_0014:stsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
IL_0019:br.sIL_001b
IL_001b:ldsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
IL_0020:stloc.0
IL_0021:ldloc.0
IL_0022:ldc.i4.s20
IL_0024:callvirtinstance!1classArtech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)
IL_0029:pop
IL_002a:ret
}
//
endofmethodProgram::Main
和我们上面一节的IL对比,是否出奇地相似。所用我们可以说,我们在第一节中Named Delegate和Anonymous Method Delegate是等效的。
接下来我们通过Lambda Expression实现上面的功能。
三、Lambda Expression
下面是通过Lambda Expression实现上面相同功能的Code:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
Artech.LambdaExpression
{
classProgram

{
staticvoidMain()

{
Function<int,bool>function3=x=>x>0;
function3(20);
}
}
delegateTResultFunction<TArgs,TResult>(TArgsargs);
}
我们通过Reflector分析编译生成的Assembly,我们发现它和通过Anonymous Method Delegate实现的完全一样:Program Class,我们发现它多了两个额外的Static成员,它们的名称都完全一样:<>9__CachedAnonymousMethodDelegate1和<Main>b__0。
这两个Static Member:<>9__CachedAnonymousMethodDelegate1和<Main>b__0的定义也于我们通过Anonymous Method Delegate实现时一模一样:
[CompilerGenerated]
private
static
Function
<
int
,
bool
>
<>
9__CachedAnonymousMethodDelegate1;
[CompilerGenerated]
private
static
bool
<
Main
>
b__0(
int
args)
{
return(args>0);
}
我们进一步来看看Main Method 的IL。
.method
private
hidebysig
static
void
Main()cilmanaged
{
.entrypoint
//Codesize43(0x2b)
.maxstack3
.localsinit([0]classArtech.LambdaExpression.Function`2<int32,bool>function3)
IL_0000:nop
IL_0001:ldsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
IL_0006:brtrue.sIL_001b
IL_0008:ldnull
IL_0009:ldftnboolArtech.LambdaExpression.Program::'<Main>b__0'(int32)
IL_000f:newobjinstancevoidclassArtech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
nativeint)
IL_0014:stsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
IL_0019:br.sIL_001b
IL_001b:ldsfldclassArtech.LambdaExpression.Function`2<int32,bool>Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
IL_0020:stloc.0
IL_0021:ldloc.0
IL_0022:ldc.i4.s20
IL_0024:callvirtinstance!1classArtech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)
IL_0029:pop
IL_002a:ret
}
//
endofmethodProgram::Main
和上面通过
Anonymous Method Delegate 实现的时候完全是一样的。四、Conclusion
现在我们可以得出结论了,Lambda Expression本质上是一个Anonymous Method Delegate,这个Delegate的匿名性仅仅针对Programming language而言,编译器会为它生成一个Named delegate和一个它指向的Method。这个两个额外生成的对象作为使用Anonymous Method Delegate对应的Class的Static Method而存在。从本质上讲和一般的Delegate并没有本质的区别。所以上面我们分别通过Named delegate、Anonymous method delegate和Lambda Expression实现的3个方式是等效的。
C# 3.x相关内容:
[原创]深入理解C# 3.x的新特性(1):Anonymous Type
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part I
1168

被折叠的 条评论
为什么被折叠?



