zoukankan      html  css  js  c++  java
  • c#3.0新特性(二):Lambda表达式

    一、由一个简单的排序想到的
    在javascript中,数组对象Array有一个sort方法,在javascript:内置对象学习笔记二 我已经整理总结了一下,现在拿出来重温一遍:
    Code
    也可以这么写:
    Code
    js的排序简单直观,在c#里,也可以实现类似的排序,而且更强大和方便,比如c#中的泛型排序方法,常见的List<T>就有如下Sort方法:
    public void Sort();
    public void Sort(Comparison<T> comparison);
    public void Sort(IComparer<T> comparer);
    public void Sort(int index, int count, IComparer<T> comparer);
    现以List<T>举一个应用实例:
    Code
    我们还可以用下面的匿名方法代替上面的BookComparasion类的方法(虽然我不喜欢匿名方法):
    Code
    下面的代码可以轻松实现相同的功能:
    Code
    上面的代码注释已经写的很清楚,没错,现在向“主流”靠拢,就是Lambda表达式了。
    二、Lambda表达式学习笔记
    在C#2.0中引入了匿名方法允许在期望出现委托的时候以“内联”的代码替代之。尽管匿名方法提供了函数式编程语言中的很多表达能力,但匿名方法的语法实在是太“不近人情”了,并且很不自然,用多了会造成阅读上的困难。举例来说:
    Code
    上面的匿名委托我们可以简化为Lambda表达式来表示。实际上Lambda表达式的本质是匿名方法,也即是当编译我们的程序代码时,编译器会自动帮我们将Lambda表达式转换为匿名方法。看下改进后的代码:
    下面系统学习Lambda表达式的相关语法和应用:
    1、创建
    Lambda表达式的书写方式是一个参数列表后跟“=>”记号,然后跟一个表达式或一个语句块,即Lambda表达式的语法格式为:参数列 => 语句或语句块。
    (param1, param2, …paramN) =>
    {
    statement1;
    statement2;

    statementN;
    return(lambda_expression_return_type);

    2、关于“参数列

    Lambda表达式的参数列可以具有显式的或隐式的类型。在一个具有显式类型的参数列表中,每个参数的类型都是显式声明的。在一个具有隐式类型的参数列表中,参数的类型是从Lambda表达式出现的上下文中推断出来的——具体来说,是当Lambda表达式被转换为一个兼容的委托类型时,该委托类型提供了参数的类型。
    当Lambda表达式只有一个具有隐式类型的参数时,参数列表中的括号可以省略。即:(param) => expression可以简写为:param => expression。
    注意,参数列中可包含任意个参数(与委托对应),如果参数列中有0个或1个以上参数,则必须使用括号括住参数列,举例如下:
    () => Console.Write("0个参数");

    i => Console.Write("1个参数时参数列中可省略括号,值为:{0}", i);

    (x, y) => Console.Write("包含2个参数,值为:{0}:{1}", x, y);

    而“语句或语句块”中如果只有一条语句,则可以不用大括号括住,否则则必须使用大括号,如下所示:

     //只有一条语句,则可以不用大括号括住
     i => Console.Write("只有一条语句");

     i => { Console.Write("使用大括号的表达式"); };

     //两条语句时必须要大括号
     i => { i++; Console.Write("两条语句的情况"); };
    如果“语句或语句块”有返回值时,如果只有一条语句则可以不写“return”语句,编译器会自动处理,否则必须加上,如下示例:

    Code
    3、遵循规则

    Lambda表达式是委托的实现方法,所以必须遵循以下规则:
    (1)Lambda表达式的参数数量必须和委托的参数数量相同;
    2)如果委托的参数中包括有ref或out修饰符,则Lambda表达式的参数列中也必须包括有修饰符;
    (3)如果委托有返回类型,则Lambda表达式的语句或语句块中也必须返回相同类型的数据;
    4)如果委托有几种数据类型格式而在Lambda表达式中编译器无法推断具体数据类型时,则必须手动明确数据类型。
    示例如下:

    Code

     小结: C# 2.0规范中提到的匿名方法规范同样适用于Lambda表达式。Lambda表达式是匿名方法在功能行上的超集,提供了下列附加的功能:

    (1)Lambda表达式允许省略参数类型并对其进行推断,而匿名方法要求参数类型必须显式地声明。
    (2)Lambda表达式体可以是表达式或语句块,而匿名方法体只能是语句块。
    (3)在类型参数推导和方法重载抉择时,Lambda表达式可以被作为参数传递。
    4)以一个表达式作为表达式体的Lambda表达式可以被转换为表达式树
    4、Lambda表达式的转换
    两个代表:下面行文中的D代表Delegate(委托类型),L代表Lambda表达式
    和匿名方法表达式类似,Lambda表达式可以归类为一种拥有特定转换规则的值。这种值没有类型,但可以被隐式地转换为一个兼容的委托类型。
    特别地,当满足下列条件时,委托类型兼容于Lambda表达式:
    (1) D和L具有相同数量的参数;
    (2) 如果L具有显式类型的参数列表,D中每个参数的类型和修饰符必须和L中相应的参数完全一致;
    (3) 如果L具有隐式类型的参数列表,则D中不能有ref或out参数;
    (4) 如果D具有void返回值类型,并且L的表达式体是一个表达式,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个可接受为statement-expression的有效表达式;
    (5) 如果D具有void返回值类型,并且L的表达式体是一个语句块,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个有效语句块,并且该语句块中不能有带有表达式的return语句;
    (6) 如果D的返回值类型不是void,并且L的表达式体是一个表达式,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个可以隐式转换为D的返回值类型的有效表达式;
    (7) 如果D的返回值类型不是void,并且L的表达式体是一个语句块,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个有效的语句块,该语句块不能有可达的终点
    (即必须有return语句),并且每个return语句中的表达式都必须能够隐式转换为D的返回值类型。
    (ps:ms这里的约束也太多了吧^_^。哎,不管了,先抄下来,以后会熟练运用就行了)

    三、一个需要注意的地方
    看下面的代码:

    Code

    运行试一下看看结果,如果你以前碰到过js里的闭包问题,相信你不会大惊小怪(而且可能已经知道了问题的原因),但是,如果你从来没有碰到过这种情况,是不是令你大吃一惊?!输出的竟然不是0,1,2,而是三个3,oh,my god。紧接着,立刻,你会大胆想到这里的list在Add方法执行的地方Add进去的是一个引用类型(这里是lambda表达式()=>i),它们执行的结果共同指向值为3的同一个引用地址!
    没错,我们详细分析一下:
    1、我们首先定义一个list,其存储格式为func<int>,即返回int型的代理;然后,用for循环将i封装进lambda表达式,并加入到该list中,最后,用foreach循环输出结果。
    2、因为lambda表达式实质就是个委托,也就指向一个匿名函数,所以,在foreach输出的时候,使用item()来调用它,让它所指向的函数执行。
    至于第2步中item()执行的结果为什么都是3,原因是这样的:
    (1)在for循环中,只能有一个 i 变量。即在第一次循环时,i 的地址就分配好了(注意了,这里i的地址第一次分配后是不变的),不会因为循环次数的多少而发生任何改变,其改变的只能是里面装载的值。
    (2)lambda表达式在构造时, 传进去的是变量的地址,而不是具体值。只有当真正执行这个lambda表达式时,才会去确定它的值。这就是为什么上面的例子中,其结果均为3(for循环在最后,当i=2时,i又加了1)。

    那么如何解决这个问题?解决方案很简单,就是在for循环中,定义一临时变量tmpNum,存储i的值即可。因为编译器会对该临时变量重新分配内存,这样,每次循环,都重新分配新的内存,就不会有这个问题了。
    最后把这个“功德圆满”的解决方案贴出来:

    Code

    好了,Lambda表达式先学到这里,多读多写熟练就不害怕了,继续学习去也。

    Code


    作者:Jeff Wong
    出处:http://jeffwongishandsome.cnblogs.com/
    本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。

  • 相关阅读:
    Vim
    一文搞定Samba云服务器配置(阿里云)CentOS8.3
    第四课 婴儿是如何思考的 思维的发展历程
    C++/VS基础篇
    Windows下Qt VS 打包程序 到他人电脑安装运行出现的问题
    第三课 斯金纳
    第二课基础 弗洛伊德
    C++中头文件和实现文件的关系
    字符串 数字 转换
    Ucore lab1实验报告
  • 原文地址:https://www.cnblogs.com/jeffwongishandsome/p/1460102.html
Copyright © 2011-2022 走看看