Artech

Develop every application as an art using the most suitable technologies!

常用链接

统计

积分与排名

网上邻居

我的博文系列

最新评论

[原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression

较之前一个版本,对于C# 3.xVB 9来说,LINQ是最具吸引力的。基本上很多的新的特性都是围绕着LINQ的实现来设计的。借助Extension Method,我们可以为LINQ定义一系列的Operator。通过Lambda Expression我们可以为LINQ编写更加简洁的查询。我们可以说这些新的特性成就了LINQ,也可以说这些新特性就是为了实现LINQ而产生,但是我们应该明白,对于这些新引入的特性,LINQ并非他们唯一的用武之地,在一般的编程中,我们也可以使用它们。

继上一章,介绍Extension Method之后,我们接着来介绍另一个重要的特性:Lambda Expression。在前面的两篇文章中,我一再在强调这样的一个概念:C# 3.x新引入的这些特性仅仅反映在Programming Language和相应的Compiler层面。通过编译生成的AssemblyIL和原来并没有本质的改变。从这个意义上讲,所有的这些其实是编译器给我们玩得障眼法而已。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
{
    
class Program
    
{
        
static void Main()
        
{
            _namedMethodDelegate 
= new Function<intbool>(SomeMethod);
            Function
<intbool> function1 = _namedMethodDelegate;
            function1(
20);     

        }

        
private static Function<intbool> _namedMethodDelegate;

        
private static bool SomeMethod(int args)
        
{
            
return (args > 0);
        }


    }


    
delegate TResult Function<TArgs, TResult>(TArgs args);

}

上面的例子很简单,先定一个Generic Delegate Function。在Program Class中定义一个StaticFunction字段_namedMethodDelegate和与之对应的MethodSomeMethod,判断输入的数字是否大于零。在Main中实例化_namedMethodDelegate,并调用它。

我们通过IL Disassembler这个Utility来看看Main方法的IL代码。为了让对IL Instruction不是很了解的读者更加容易地理解整个执行过程,我加了简单注释。对于那些希望进一步了解整个MSIL Instruction列表的读者,可以参考:MSIL Instruction Table

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  
// Code size       34 (0x22)
  .maxstack  3
  .locals init ([
0class Artech.LambdaExpression.Function`2<int32,bool> function1)//Initialize function1
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  ldftn      
bool Artech.LambdaExpression.Program::SomeMethod(int32)//Pushes the method pointer referenced by SomeMethod. 
  IL_0008:  newobj     instance void class Artech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
                                                                                                 native 
int)//Initializer a Artech.LambdaExpression.Function delegate instance.
  IL_000d:  stsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::_namedMethodDelegate//Stores a static field: _namedMethodDelegate
  IL_0012:  ldsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::_namedMethodDelegate//Pushes the static field(_namedMethodDelegate)  of an object Static .
  IL_0017:  stloc.0   //Pop the first local variable 
  IL_0018:  ldloc.0   //Pushes the first local variable 
  IL_0019:  ldc.i4.s   20 //Pushes specified 8-bit value (20) as 32-bit 
  IL_001b:  callvirt   instance !1 class Artech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)//Calls virtual method of delegate instance. 
  IL_0020:  pop
  IL_0021:  ret
}
 // end of method Program::Main

对于Delegate,我无须再作深入的介绍,相信大家早已了如指掌。在这里需要着重提出是,上面介绍的内容将是后续部分的基础,通过后面的对Anonymous MethodLambda expression介绍,你会发现它们生成的代码结构和上面的是非常相似的。

二、  Anonymous Method Delegate

Anonymous MethodC# 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
{
    
class Program
    
{
        
static void Main()
        
{
            Function
<intbool> function2 = delegate(int args)
            
{
                
return args > 0;
            }
;
            function2(
20);   
        }

    }

    
delegate TResult Function<TArgs, TResult>(TArgs args);
}

我们通过Reflector分析编译生成的Assembly,我们发现它具有下面的结构。进一步分析Program Class,我们发现它多了两个额外的Static成员:<>9__CachedAnonymousMethodDelegate1<Main>b__0。这是编译器的功劳。

下面分别是<>9__CachedAnonymousMethodDelegate1<Main>b__0的定义:

[CompilerGenerated]

private static Function<intbool> <>9__CachedAnonymousMethodDelegate1;

[CompilerGenerated]

private static bool <Main>b__0(int args)

{

    
return (args > 0);

}



是不是我我们上面一节定义的_namedMethodDelegateSomeMethod这个两个静态成员一样?  

我们进一步分析Main MethodIL

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  
// Code size       43 (0x2b)
  .maxstack  3
  .locals init ([
0class Artech.LambdaExpression.Function`2<int32,bool> function2)
  IL_0000:  nop
  IL_0001:  ldsfld     
class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
  IL_0006:  brtrue.s   IL_001b
  IL_0008:  ldnull
  IL_0009:  ldftn      
bool Artech.LambdaExpression.Program::'<Main>b__0'(int32)
  IL_000f:  newobj     instance 
void class Artech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
                                                                                                 native 
int)
  IL_0014:  stsfld     
class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
  IL_0019:  br.s       IL_001b
  IL_001b:  ldsfld     
class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
  IL_0020:  stloc.
0
  IL_0021:  ldloc.
0
  IL_0022:  ldc.i4.s   
20
  IL_0024:  callvirt   instance 
!1 class Artech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)
  IL_0029:  pop
  IL_002a:  ret
}
 // end of method Program::Main

和我们上面一节的IL对比,是否出奇地相似。所用我们可以说,我们在第一节中Named DelegateAnonymous Method Delegate是等效的。

接下来我们通过Lambda Expression实现上面的功能。

三、 Lambda Expression

下面是通过Lambda Expression实现上面相同功能的Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Artech.LambdaExpression
{
    
class Program
    
{
        
static void Main()
        
{
            Function
<intbool> function3 = x => x > 0;
            function3(
20);
        }

    }


    
delegate TResult Function<TArgs, TResult>(TArgs args);

}

我们通过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<intbool> <>9__CachedAnonymousMethodDelegate1;



[CompilerGenerated]

private static bool <Main>b__0(int args)

{

    
return (args > 0);

}




我们进一步来看看Main MethodIL。
 
.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  
// Code size       43 (0x2b)
  .maxstack  3
  .locals init ([
0class Artech.LambdaExpression.Function`2<int32,bool> function3)
  IL_0000:  nop
  IL_0001:  ldsfld     
class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
  IL_0006:  brtrue.s   IL_001b
  IL_0008:  ldnull
  IL_0009:  ldftn      
bool Artech.LambdaExpression.Program::'<Main>b__0'(int32)
  IL_000f:  newobj     instance 
void class Artech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
                                                                                                 native 
int)
  IL_0014:  stsfld     
class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
  IL_0019:  br.s       IL_001b
  IL_001b:  ldsfld     
class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
  IL_0020:  stloc.
0
  IL_0021:  ldloc.
0
  IL_0022:  ldc.i4.s   
20
  IL_0024:  callvirt   instance 
!1 class Artech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)
  IL_0029:  pop
  IL_002a:  ret
}
 // end of method Program::Main

和上面通过Anonymous Method Delegate实现的时候完全是一样的。

四、Conclusion
 

现在我们可以得出结论了,Lambda Expression本质上是一个Anonymous Method Delegate,这个Delegate的匿名性仅仅针对Programming language而言,编译器会为它生成一个Named delegate和一个它指向的Method。这个两个额外生成的对象作为使用Anonymous Method Delegate对应的ClassStatic Method而存在。从本质上讲和一般的Delegate并没有本质的区别。所以上面我们分别通过Named delegateAnonymous method delegateLambda Expression实现的3个方式是等效的。

C# 3.x相关内容:
[原创]深入理解C# 3.x的新特性(1):Anonymous Type
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part I
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part II
[原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
[原创]深入理解C# 3.x的新特性(4):Automatically Implemented Property
[原创]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer

posted on 2007-08-22 13:44 Artech 阅读(4152) 评论(26)  编辑 收藏 网摘 所属分类: A. .NET FrameworkE. LINQ

评论

#1楼 2007-08-22 13:59 Anders[未注册用户]

有好一阵没见LZ发帖了,欢迎归来:)   回复  引用    

#2楼 2007-08-22 14:00 C######[未注册用户]

LZ还是一如既往地高质量啊,赞一个!   回复  引用    

#3楼[楼主] 2007-08-22 14:01 Artech      

@Anders
月初离职,给自己放了个长假,出去玩去了,昨天刚刚回来:)
  回复  引用  查看    

#4楼[楼主] 2007-08-22 14:02 Artech      

@C######
谢谢关注,我只希望我的文章多别人有一定的价值;)
  回复  引用  查看    

#5楼 2007-08-22 16:51 Anytao      

欢迎归来,用心的总是最好的,给别人价值也就是给自己加值。呵呵。   回复  引用  查看    

#6楼[楼主] 2007-08-22 16:54 Artech      

@Anytao
:)
  回复  引用  查看    

#7楼 2007-08-22 18:12 Lostinet      

如果不提出Linq Expression的情况, 你的文章会很容易误导人.

与其说Lambda是为了简化delegate{}的写法, 还不如说DotNet引入Lambda是完全为了Linq Expression.
  回复  引用  查看    

#8楼[楼主] 2007-08-22 20:08 Artech      

@Lostinet
我的观点是,尽管Lambda Expression尽管在设计的时候可能是基于实现LINQ来设计的,但是我们把不能Lambda Expression完全限制到LINQ的范畴。我觉得这种理解是狭隘的。我反而觉得谈起Lambda Expression,就必谈LINQ才会让人误导。

所以我才完全撇开LINQ谈Lambda Expression,本文旨在讲述Lambda Expression的本质,而不是讲其应用。
  回复  引用  查看    

#9楼 2007-08-23 09:36 吕昆[未注册用户]

文章介绍得比较详细!!

我也是.NET的爱好者!欢迎您也到我的博客做客!

http://www.10000o.com.cn
  回复  引用    

#10楼[楼主] 2007-08-23 09:47 Artech      

@吕昆
你的博客作得挺漂亮,以后一定多多光顾:)
  回复  引用  查看    

#11楼 2007-08-23 11:45 周银辉      

Good   回复  引用  查看    

#12楼[楼主] 2007-08-23 12:35 Artech      

@周银辉
3x,:)
  回复  引用  查看    

#13楼 2007-08-23 18:53 hhh[未注册用户]

看不懂啊:《   回复  引用    

#14楼[楼主] 2007-08-23 19:03 Artech      

@hhh
哦?
  回复  引用  查看    

#15楼 2007-08-24 16:42 Bird[未注册用户]

C#3.0的新特性都是围绕LINQ这个主题设计的,Lambda只是其中一个花哨的小动作而已,让AnonymousMethod看起来更加简洁、直接。

To Artech: 楼主辛苦了,好好找工作!你一定行的。
  回复  引用    

#16楼[楼主] 2007-08-24 17:30 Artech      

@Bird
谢谢Bird
  回复  引用  查看    

#17楼 2007-11-06 09:57 dlbrant[未注册用户]

博克很漂亮,但黑底白字看久了比较晕,
建议搂主改改
  回复  引用    

#18楼[楼主] 2007-11-06 10:29 Artech      

@dlbrant
还是使用我原来的模板好^_^
  回复  引用  查看    

#19楼 2007-11-08 14:34 pk的眼泪      

分析到位,好文.
就如楼主所说 "我们不应该把Lambda Expression完全限制到LINQ的范畴"
因为它本身就是已经存在的东西,
只不过是换个写法,换件衣服罢了.
  回复  引用  查看    

#20楼 2008-01-19 14:50 共同学习,共同进步      

thanks very much , you are real dotnet new feature evangelist. by reading and thinking what you have been writing so far , I have the well-understand , thanks again.   回复  引用  查看    

#21楼 2008-02-01 14:07 永春      

好文
虽然还没有做过C#3.x的项目,提前看看也不错
  回复  引用  查看    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 865247




相关文章:

相关链接: