Expression经验之前言,深入理解C
分类:计算机编程

对于C#中的Expression特性想必从事C#开发的同学都不会陌生,网上和园子里都有很多的好的文章介绍。我想也没有必要再去写一些文章去介绍,科普或是从入门到精通之类的。

较之前一个版本,对于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。

借助于Expression, 我们可以写些很优雅(至少个人觉得)的代码。

接下来,我将通过一个简单的Demonstration,由浅入深地分析Lambda Expression,看看编译器到底会编译生成怎样的额外的Code,他们的IL又是如何。

例于开发WPF的同学们每天要接触的ViewModel中,

一、Named Delegate

OnPropertyChanged(() => Name);

在上面,我说了Lambda Expression本质上就是一个Delegate,我们先不直接来介绍Lambda Expression, 我们先来看看我们最为熟悉的Delegate的例子: 

要比

图片 1using System;
图片 2using System.Collections.Generic;
图片 3using System.Linq;
图片 4using System.Text;
图片 5
图片 6namespace Artech.LambdaExpression
图片 7图片 8图片 9{
图片 10    class Program
图片 11图片 12    图片 13{
图片 14        static void Main()
图片 15图片 16        图片 17{
图片 18            _namedMethodDelegate = new Function<int, bool>(SomeMethod);
图片 19            Function<int, bool> function1 = _namedMethodDelegate;
图片 20            function1(20);     
图片 21
图片 22        }
图片 23        private static Function<int, bool> _namedMethodDelegate;
图片 24
图片 25        private static bool SomeMethod(int args)
图片 26图片 27        图片 28{
图片 29            return (args > 0);
图片 30        }
图片 31
图片 32    }
图片 33
图片 34    delegate TResult Function<TArgs, TResult>(TArgs args);
图片 35
图片 36}

OnPropertyChanged("Name")

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

优雅。并且更多的好处是可以在编译时检查错误,尤其是在Property名字被改变后。

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

 

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

好了,对Expression的夸奖就不多说了。以下是我想分享的经验:

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

Expression经验之一:合并LambdaExpression

二、  Anonymous Method Delegate

Expression经验之二:LambdaExpression变换

Anonymous Method是C# 2.0引入的一个非常好用的功能。通过Anonymous Method,我们可以Delegate的实现直接以Inline的方式放入Delegate对象使用的位置,而无须再繁琐地创建一个Delegate,并通过定义在某个Class中具有相同申明的Method来事例化这个Delegate Instance,最后才将这个delegate instance传入需要调用的Method。

 

我们现在通过Anonymous Method来简化上面的代码。

图片 60using System;
图片 61using System.Collections.Generic;
图片 62using System.Linq;
图片 63using System.Text;
图片 64
图片 65namespace Artech.LambdaExpression
图片 66图片 67图片 68{
图片 69    class Program
图片 70图片 71    图片 72{
图片 73        static void Main()
图片 74图片 75        图片 76{
图片 77            Function<int, bool> function2 = delegate(int args)
图片 78图片 79            图片 80{
图片 81                return args > 0;
图片 82            };
图片 83            function2(20);   
图片 84        }
图片 85    }
图片 86    delegate TResult Function<TArgs, TResult>(TArgs args);
图片 87}
图片 88

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

图片 89

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

图片 90[CompilerGenerated]

图片 91private static Function<int, bool> <>9__CachedAnonymousMethodDelegate1;

图片 92[CompilerGenerated]

图片 93private static bool <Main>b__0(int args)

图片 94图片 95图片 96{

图片 97    return (args > 0);

图片 98}

图片 99

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

我们进一步分析Main Method的IL。

图片 100.method private hidebysig static void  Main() cil managed
图片 101图片 102图片 103{
图片 104  .entrypoint
图片 105  // Code size       43 (0x2b)
图片 106  .maxstack  3
图片 107  .locals init ([0] class Artech.LambdaExpression.Function`2<int32,bool> function2)
图片 108  IL_0000:  nop
图片 109  IL_0001:  ldsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
图片 110  IL_0006:  brtrue.s   IL_001b
图片 111  IL_0008:  ldnull
图片 112  IL_0009:  ldftn      bool Artech.LambdaExpression.Program::'<Main>b__0'(int32)
图片 113  IL_000f:  newobj     instance void class Artech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
图片 114                                                                                                 native int)
图片 115  IL_0014:  stsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
图片 116  IL_0019:  br.s       IL_001b
图片 117  IL_001b:  ldsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
图片 118  IL_0020:  stloc.0
图片 119  IL_0021:  ldloc.0
图片 120  IL_0022:  ldc.i4.s   20
图片 121  IL_0024:  callvirt   instance !1 class Artech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)
图片 122  IL_0029:  pop
图片 123  IL_002a:  ret
图片 124} // end of method Program::Main
图片 125

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

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

三、 Lambda Expression

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

图片 126using System;
图片 127using System.Collections.Generic;
图片 128using System.Linq;
图片 129using System.Text;
图片 130
图片 131namespace Artech.LambdaExpression
图片 132图片 133图片 134{
图片 135    class Program
图片 136图片 137    图片 138{
图片 139        static void Main()
图片 140图片 141        图片 142{
图片 143            Function<int, bool> function3 = x => x > 0;
图片 144            function3(20);
图片 145        }
图片 146    }
图片 147
图片 148    delegate TResult Function<TArgs, TResult>(TArgs args);
图片 149
图片 150}
图片 151

我们通过Reflector分析编译生成的Assembly,我们发现它和通过Anonymous Method Delegate实现的完全一样:Program Class,我们发现它多了两个额外的Static成员,它们的名称都完全一样:<>9__CachedAnonymousMethodDelegate1和<Main>b__0。

图片 152

这两个Static Member:<>9__CachedAnonymousMethodDelegate1和<Main>b__0的定义也于我们通过Anonymous Method Delegate实现时一模一样:

图片 153[CompilerGenerated]

图片 154private static Function<int, bool> <>9__CachedAnonymousMethodDelegate1;

图片 155

图片 156[CompilerGenerated]

图片 157private static bool <Main>b__0(int args)

图片 158图片 159图片 160{

图片 161    return (args > 0);

图片 162}

图片 163

我们进一步来看看Main Method的IL。  

图片 164.method private hidebysig static void  Main() cil managed
图片 165图片 166图片 167{
图片 168  .entrypoint
图片 169  // Code size       43 (0x2b)
图片 170  .maxstack  3
图片 171  .locals init ([0] class Artech.LambdaExpression.Function`2<int32,bool> function3)
图片 172  IL_0000:  nop
图片 173  IL_0001:  ldsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
图片 174  IL_0006:  brtrue.s   IL_001b
图片 175  IL_0008:  ldnull
图片 176  IL_0009:  ldftn      bool Artech.LambdaExpression.Program::'<Main>b__0'(int32)
图片 177  IL_000f:  newobj     instance void class Artech.LambdaExpression.Function`2<int32,bool>::.ctor(object,
图片 178                                                                                                 native int)
图片 179  IL_0014:  stsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
图片 180  IL_0019:  br.s       IL_001b
图片 181  IL_001b:  ldsfld     class Artech.LambdaExpression.Function`2<int32,bool> Artech.LambdaExpression.Program::'<>9__CachedAnonymousMethodDelegate1'
图片 182  IL_0020:  stloc.0
图片 183  IL_0021:  ldloc.0
图片 184  IL_0022:  ldc.i4.s   20
图片 185  IL_0024:  callvirt   instance !1 class Artech.LambdaExpression.Function`2<int32,bool>::Invoke(!0)
图片 186  IL_0029:  pop
图片 187  IL_002a:  ret
图片 188} // end of method Program::Main
图片 189

和上面通过

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
[原创]深入理解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

本文由pc28.am发布于计算机编程,转载请注明出处:Expression经验之前言,深入理解C

上一篇:Pycharm的安装和使用,Adelaide装修网深入分析厨房 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • Pycharm的安装和使用,Adelaide装修网深入分析厨房
    Pycharm的安装和使用,Adelaide装修网深入分析厨房
      MapServer linux上服务安装 关于厨房中水管的安装常见的就是下水管的安装,对于下水管的安装可能很多人都不知道该如何安装,青岛装修网资深装修达人说
  • 电子商务货品库的成品设计,PHP数组内容不重复
    电子商务货品库的成品设计,PHP数组内容不重复
    多年来在做ecshop的货物仓库储存模块,分别给黄金年代款商品的两性情格组合设置仓库储存,如下图: # 手艺文书档案 每一天逛天猫和京东的时候,映着重
  • 九彩拼盘的前端技能,LayUI框架的应用
    九彩拼盘的前端技能,LayUI框架的应用
    内容: HTML 普及标签和总体性 文书档案类型申明 转义字符 网页访问无障碍(只是掌握卡塔 尔(阿拉伯语:قطر‎ CSS 常用采取器 体制生效准绳(浏览器的
  • 编制程序总计,动态目的
    编制程序总计,动态目的
    dynamic是FrameWork4.0的新特色。dynamic的现身让C#具备了弱语言类型的风味。编写翻译器在编写翻译的时候不再对项目举行检查,编译期暗中同意dynamic对象扶植
  • 动态编写翻译,在线运转
    动态编写翻译,在线运转
    千帆竞发产生c#代码的在线编辑。     在帮顾客写JAVA客商端访谈.NET达成的Webservice的示范代码发掘了一个有意思的标题。为有保持安全性,使用了wse2.0sp