较之前一个版本,对于C# 3.x和VB 9来说,LINQ是最具吸引力的。基本上很多的新的特性都是围绕着LINQ的实现来设计的。借助Extension Method,我们可以为LINQ定义一系列的Operator。通过Lambda Expression我们可以为LINQ编写更加简洁的查询。我们可以说这些新的特性成就了LINQ,也可以说这些新特性就是为了实现LINQ而产生,但是我们应该明白,对于这些新引入的特性,LINQ并非他们唯一的用武之地,在一般的编程中,我们也可以使用它们。
继上一章,介绍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的例子:
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Collections.Generic;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Linq;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Text;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace Artech.LambdaExpression
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
class Program
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static void Main()
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
_namedMethodDelegate = new Function int, bool (SomeMethod);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Function int, bool function1 = _namedMethodDelegate;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
function1(20);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private static Function int, bool _namedMethodDelegate;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private static bool SomeMethod(int args)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return (args
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
delegate TResult Function TArgs, TResult (TArgs args);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
}
上面的例子很简单,先定一个Generic Delegate :Function。在Program Class中定义一个Static的Function字段_namedMethodDelegate和与之对应的Method:SomeMethod,判断输入的数字是否大于零。在Main中实例化_namedMethodDelegate,并调用它。
我们通过IL Disassembler这个Utility来看看Main方法的IL代码。为了让对IL Instruction不是很了解的读者更加容易地理解整个执行过程,我加了简单注释。对于那些希望进一步了解整个MSIL Instruction列表的读者,可以参考:MSIL Instruction Table。
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
.method private hidebysig static void Main() cil managed
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.entrypoint
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Code size 34 (0x22)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.maxstack 3
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.locals init ([0] class Artech.LambdaExpression.Function`2 int32,bool function1)//Initialize function1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0000: nop
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0001: ldnull
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0002: ldftn bool Artech.LambdaExpression.Program::SomeMethod(int32)//Pushes the method pointer referenced by SomeMethod.
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0008: newobj instance void class Artech.LambdaExpression.Function`2 int32,bool ::.ctor(object,
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
native int)//Initializer a Artech.LambdaExpression.Function delegate instance.
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_000d: stsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program::_namedMethodDelegate//Stores a static field: _namedMethodDelegate
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0012: ldsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program::_namedMethodDelegate//Pushes the static field(_namedMethodDelegate) of an object Static .
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0017: stloc.0 //Pop the first local variable
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0018: ldloc.0 //Pushes the first local variable
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0019: ldc.i4.s 20 //Pushes specified 8-bit value (20) as 32-bit
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_001b: callvirt instance !1 class Artech.LambdaExpression.Function`2 int32,bool ::Invoke(!0)//Calls virtual method of delegate instance.
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0020: pop
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0021: ret
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
} // end of method Program::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来简化上面的代码。
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Collections.Generic;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Linq;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Text;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace Artech.LambdaExpression
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
class Program
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static void Main()
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Function int, bool function2 = delegate(int args)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return args
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
};
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
function2(20);
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
delegate TResult Function TArgs, TResult (TArgs args);
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
}
我们通过Reflector分析编译生成的Assembly,我们发现它具有下面的结构。进一步分析Program Class,我们发现它多了两个额外的Static成员: 9__CachedAnonymousMethodDelegate1和 Main b__0。这是编译器的功劳。
![](http://images.cnblogs.com/cnblogs_com/artech/csharp_v3x_03_01.JPG)
下面分别是 9__CachedAnonymousMethodDelegate1和 Main b__0的定义:
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
[CompilerGenerated]
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
private static Function int, bool 9__CachedAnonymousMethodDelegate1;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
[CompilerGenerated]
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
private static bool Main b__0(int args)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return (args
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
是不是我我们上面一节定义的_namedMethodDelegate和SomeMethod这个两个静态成员一样?
我们进一步分析Main Method的IL。
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
.method private hidebysig static void Main() cil managed
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.entrypoint
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Code size 43 (0x2b)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.maxstack 3
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.locals init ([0] class Artech.LambdaExpression.Function`2 int32,bool function2)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0000: nop
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0001: ldsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program:: 9__CachedAnonymousMethodDelegate1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0006: brtrue.s IL_001b
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0008: ldnull
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0009: ldftn bool Artech.LambdaExpression.Program:: Main b__0(int32)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_000f: newobj instance void class Artech.LambdaExpression.Function`2 int32,bool ::.ctor(object,
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
native int)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0014: stsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program:: 9__CachedAnonymousMethodDelegate1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0019: br.s IL_001b
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_001b: ldsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program:: 9__CachedAnonymousMethodDelegate1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0020: stloc.0
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0021: ldloc.0
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0022: ldc.i4.s 20
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0024: callvirt instance !1 class Artech.LambdaExpression.Function`2 int32,bool ::Invoke(!0)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0029: pop
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_002a: ret
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
} // end of method Program::Main
和我们上面一节的IL对比,是否出奇地相似。所用我们可以说,我们在第一节中Named Delegate和Anonymous Method Delegate是等效的。
接下来我们通过Lambda Expression实现上面的功能。
三、 Lambda Expression
下面是通过Lambda Expression实现上面相同功能的Code:
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Collections.Generic;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Linq;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Text;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace Artech.LambdaExpression
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
class Program
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static void Main()
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Function int, bool function3 = x = x
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
function3(20);
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
delegate TResult Function TArgs, TResult (TArgs args);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
}
我们通过Reflector分析编译生成的Assembly,我们发现它和通过Anonymous Method Delegate实现的完全一样:Program Class,我们发现它多了两个额外的Static成员,它们的名称都完全一样: 9__CachedAnonymousMethodDelegate1和 Main b__0。
![](http://images.cnblogs.com/cnblogs_com/artech/csharp_v3x_03_01.JPG)
这两个Static Member: 9__CachedAnonymousMethodDelegate1和 Main b__0的定义也于我们通过Anonymous Method Delegate实现时一模一样:
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
[CompilerGenerated]
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
private static Function int, bool 9__CachedAnonymousMethodDelegate1;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
[CompilerGenerated]
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
private static bool Main b__0(int args)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return (args
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
}
我们进一步来看看Main Method的IL。
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
.method private hidebysig static void Main() cil managed
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.entrypoint
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Code size 43 (0x2b)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.maxstack 3
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
.locals init ([0] class Artech.LambdaExpression.Function`2 int32,bool function3)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0000: nop
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0001: ldsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program:: 9__CachedAnonymousMethodDelegate1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0006: brtrue.s IL_001b
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0008: ldnull
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0009: ldftn bool Artech.LambdaExpression.Program:: Main b__0(int32)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_000f: newobj instance void class Artech.LambdaExpression.Function`2 int32,bool ::.ctor(object,
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
native int)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0014: stsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program:: 9__CachedAnonymousMethodDelegate1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0019: br.s IL_001b
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_001b: ldsfld class Artech.LambdaExpression.Function`2 int32,bool Artech.LambdaExpression.Program:: 9__CachedAnonymousMethodDelegate1
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0020: stloc.0
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0021: ldloc.0
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0022: ldc.i4.s 20
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0024: callvirt instance !1 class Artech.LambdaExpression.Function`2 int32,bool ::Invoke(!0)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_0029: pop
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
IL_002a: ret
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
} // end of method Program::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的新特性(3):从Delegate、Anonymous Method到Lambda Expression
[原创]深入理解C# 3.x的新特性(4):Automatically Implemented Property
[原创]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer
作者:蒋金楠
微信公众账号:大内老A
微博:
www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接
C#基础知识学习之 ✨ 委托的兄弟姐妹们 delegate、Action、Func、Predicate
委托 delegate、Action、Func、Predicate
简单介绍 delegate、Action、Func、Predicate
几种基础的委托结构
几种委托的实例用法
delegate使用
Action的用法
Func的用法
重构——47以明确函数取代参数(Replace Parameter with Explicit Methods)
以明确函数取代参数(Replace Parameter with Explicit Methods):你有一个函数,其中完全取决于参数值而采取不同的行为;针对该参数的每一个可用值,建立一个独立函数