zoukankan      html  css  js  c++  java
  • 匿名方法和Lambda表达式

    匿名方法本质上是一传递给委托的代码块,是使用委托的另一种方法。

    规则:

    1、匿名方法中不能使用跳转语句跳至次匿名方法的外部,反之亦然;匿名方法外部的跳转语句也不能跳转到匿名方法的内部;

    2、在匿名方法的内部不能访问不安全的代码。另外,也不能访问在匿名方法外部定义的ref和out参数。

    3、可以使用在匿名方法外部定义的其他变量。

    Lambda达式:就是匿名方法,只是语法不同。

    (param)=>expr

    param是输入参数列表,expr是一个表达式或者一系列语句。

    规则:

    1、在一个具有唯一的显示类型参数的Lambda表达式中,圆括号可以从参数列表中删除。

    2、当输入参数不唯一时,括号不能省略。

    3、输入参数列表中的各参数可以显式指定类型,也可以省略参数类型,具体类型通过类型判断机制判断。

     

    4、expr可以只包含一个计算表达式,也可以包含一系列语句,只是语句需要包含在大括号中。

    --------------------------------------------------------华丽的分割线-----------------------------------------------------------

    在我们程序中,经常有这样一些需求:
    1.       需要一个临时方法,这个方法只会使用一次,或者使用的很少。
    2.       这个方法的方法体很短,以至于比方法声明都短,写起来实在没劲(我将其称之为“一句话方法”)
    没办法,这样的方法写起来真是吃力不讨好,比如一些按钮事件处理中,有些按钮点击就是弹出一个对话框,或者调用一下别的什么方法。比如下面的代码:

    this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
    private void btnRefresh_Click(object sender, EventArgs e)
    {
        BindData();
    }
    这个”Refresh”按钮就是做一下调用一下BindData()数据绑定的方法,为此我们不得不写一个新方法。好了,C# 2.0为我们提供了匿名方法:
    this.btnRefresh.Click += delegate(object sender, EventArgs e) { BindData(); };
    没劲的代码没了。想知道这种写法的幕后黑手么?
    其实编译器还是在我们的后面干了一件龌龊的事情:它为我们产生了一个新的方法,它只是表面上为我们节省了代码。
    privatevoid<Test>b__0(object sender, EventArgs e)
    {
        this.BindData();
    }
    看看这个编译器产生的方法的名称:
    <Test>b_0Test是这个匿名方法所放置的地方(因为这个按钮的时间我是放在一个Test方法里的)
    还有一点需要注意的是,如果这个匿名方法是在实例方法里使用,那么编译器为我们生成的幕后方法也是实例方法,否则就是静态方法了.
    是不是觉得匿名方法这东西很不错,减少了很多代码阿,但是匿名方法的使用还并不人性化,什么是人性化呢?比如你可以用自然的语言将程序代码读出来,这样才算人性化了..net 2.0System.Collections.Generic命名空间下List<T>里有一些新增的方法。比如Find,如果使用匿名方法我们如何调用呢:
    books.Find(delegate(Book book){return book.Price < 50;});
    代码是很简单,但是却无法朗读出来,来看看Lambda表达式的写法:
    books.Find(book=>book.Price<50);这个Lambda表达式就可以这样阅读出来了:给你一本书,如果它的价格小于50则返回true
    好了,那我们就走进Lambda表达式吧:

     

        将使用了Lambda表达式的程序集反编译后,我们发现,它实际上和匿名方法没有什么不同。Lambda的输入参数就对应着delegate括号里面的参数,由于Lambda表达式可以推断参数的类型,所以这里的参数无需声明。Lambda操作符读作”Goes to”,它后面紧跟着表达式或者是语句块(这点和匿名方法也不同,匿名方法只能使用语句块而不能使用表达式),下面我就用实例来说明一下有那些类型的Lambda表达式:

    //x的类型省略了,编译器可以根据上下文推断出来,后面跟着的是表达式
    x => x+1 
    deleage(int x){return x+1;}
    
    //后面跟着的是语句块
    x=>{return x+1;} 
    delegate(int x){return x+1;}
    
    //输入参数也可以带类型,带类型后别忘记小括号哦
    (int x) => x+1 
    delegate(int x){return x+1;}
    
    //也可以多个输入参数,逗号分隔,别忘记小括号
    (x,y) => x+y 
    delegate(int x,int y){return x+y;}
    
    //无参的也行
    () => 1
    delegate(){return 1;}

        对于Lambda表达式来说她的用法就是如此,但是在Lambda背后却有很多的故事和玄机。用Lambda表达式可以构建表达式树,而表达式树对于Linq来说就像树根对于树一样重要。在这里就不讨论表达式树的问题了,这个东西也不是三言两语能够说清楚的,等待时机成熟的时候我们再来进一步讨论。

        Lambda实际上源远流长,我们现在使用的机器都是冯-诺依曼体系的,属于图灵机,在那之前还有一种称作λ演算的理论,但是图灵机由于先被实现出来,所以大行其道,λ演算后来成就了函数式编程语言特别是Lisp,在函数式编程语言里函数是第一等元素,函数的参数,函数的返回值都是函数,程序没有变量,函数嵌套函数。而且函数式编程语言一直存在于象牙塔中,所以在工业界并没有得到通用,不过近年来工业界比较喜欢“复古”风格,所以函数式编程语言也慢慢的走上了历史的舞台。函数式编程能解决一些命令式编程难以解决的问题(或者解决起来非常麻烦)C#要做到函数风格编程怎么办?靠原来的方法定义的方式肯定是不可行的,2.0的匿名方法从某种程序上来说解决了这个问题,但还是不够,3.0里的Lambda终于很好的解决了,一个Lambda就是一个delegate,一个delegate指向一个方法,现在我们使用Lambda也能简单的将方法作为参数传递了,还可以层层嵌套,都是很简单的事情了。

        另外,匿名方法可以省略参数列表,编译器可以自动推断,lambda表达式做不到这一点。

    --------------------------------------------------------华丽的分割线-----------------------------------------------------------

        匿名方法在C#中应用十分广泛,因为委托作为函数参数是件非常平常的事情。在定义简单的事件处理过程时,我们同样可以使用匿名方法。比如WCF中服务寄宿成功后绑定事件:

    ServiceHost host = new ServiceHost(typeof(FileTransferImpl));
    host.Opened += delegate(object sender, EventArgs e)
    {
        Console.WriteLine("Service Opened.");
    };

        匿名方法可以很方便地使用本地变量,这与单独定义的命名方法相比,能够简化编程。比如上文的例子中,假如Main函数里面定义了一个整型本地变量(局部变量)number,那么可以在delegate (string name)这一匿名方法定义中使用number变量。

        上文提到,在定义匿名方法的时候,连参数列表都可以省略。因为编译器可以根据委托的签名来确定函数的签名,然后只要再给函数起个名字就可以了。下面的代码演示了这种使用方式:

    delegate void IntDelegate(int x);
    
    // 带参数的定义方式   
    IntDelegate d2 = delegate(int p) { Console.WriteLine(p); };
    // 不带参数的定义方式(当然也没带返回值)   
    IntDelegate d3 = delegate { Console.WriteLine("Hello."); };

    在使用不带参数和返回值的匿名方法定义时,需要注意以下两点:

    1. 如果在你的匿名方法中需要对参数进行处理,那么你不能使用不定义参数列表的声明方式。也就是在定义匿名方法的时候,需要给出参数列表
    2. 不带参数和返回值的匿名方法,可以被具有任何形式签名的委托所指代

    上述第一点显而易见,因为你没有定义参数列表,也就没有办法使用参数;要说明第二点,我们可以看下面的代码:

     1     class Program
     2     {
     3         delegate void IntDelegate(int x);
     4         delegate void StringDelegate(string y);
     5 
     6         static void Output(IntDelegate id)
     7         {
     8             //Do output
     9         }
    10         static void Output(StringDelegate sd)
    11         {
    12             //Do output
    13         }
    14         static void Main(string[] args)
    15         {
    16             /*  
    17              * ERROR: The call is ambiguous between  
    18              *        Output(IntDelegate)  
    19              *              and  
    20              *        Output(StringDelegate)  
    21              */
    22             Output(delegate { });
    23         }
    24     }

        上面的代码没法编译通过,因为编译器不知道应该将delegate { }这一匿名方法还原为由IntDelegate指代的函数,还是还原为由StringDelegate指代的函数。此时只能显式给定参数列表,以便让编译器知道,我们究竟是想调用哪个Output函数。

    参考文献:
    http://blog.csdn.net/jfkidear/article/details/19421175
    http://kb.cnblogs.com/page/42579/
    http://www.cnblogs.com/daxnet/archive/2008/11/12/1687011.html

  • 相关阅读:
    Python的包管理工具Pip
    [Reactive Programming] RxJS dynamic behavior
    [Reactive Programming] Using an event stream of double clicks -- buffer()
    [RxJS + AngularJS] Sync Requests with RxJS and Angular
    [Javascript] An Introduction to JSPM (JavaScript Package Manager)
    [Angular 2] 9. Replace ng-modle with #ref & events
    [React] Intro to inline styles in React components
    [AngualrJS + Webpack] Production Source Maps
    [AngularJS + Webpack] Uglifying your JavaScript
    [AngularJS + Webpack] Production Setup
  • 原文地址:https://www.cnblogs.com/ChrisLi/p/4189820.html
Copyright © 2011-2022 走看看