zoukankan      html  css  js  c++  java
  • Lambda高手之路第一部分

    转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847574.html

    介绍

    Lambda表达式是使代码更加动态,易于扩展并且更加快速(看完本文你就知道原因了)的强有力的工具。也可以用来降低潜在的错误。同时可以利用静态输入和智能提示,就像VS里一样。

    Lambda表达式在.net framework 3.5中提出来。并且在LINQ和ASP.NET MVC内部的一些技术中扮演了相当重要的角色。如果你考虑一下ASP.NET MVC中各类控件的实现。你就发现。奥妙就是他们大多使用了Lambda表达式。和Lambda表达式一起,使用Html扩展方法将会使得在后台创建模型 成为可能。

    本文会讲到如下的知识。

    1.简短的介绍-Lambda表达式是什么,以及为什么和匿名方法不同(之前我们使用的)
    2.走近Lambda表达式的性能-在哪些情况下比起标准方法,Lambda会提高/损失性能
    3.深入-Lambda表达式在MSIL代码中是什么样
    4.一些来自JS世界的模式映射到C#中
    5.那些能够提高性能,并且代码看起来相当舒服的使用Lambda的情况。
    6.一些我提出的新模式-当然有可能别人也提出来了。但这是我的思考结果。

    如果你期望本文是一篇入门教程我可能要让你失望了,除非你真的很优秀并且很聪明,当然我不是这种人,所以我也想提前声明一下:为了读懂这篇文章你可能需要C#的一些高级知识,并且对C#比较了解。

    你应该期望本文试着解释一些事情给你,也会解释一些有趣的问题,至少对我来说是这样的。最后我会展示一些实际的例子和模式,如我所说,Lambda表达式简化了很多情况。因此写显式的模式很有用。

    背景知识-什么是Lambda表达式

    在C#1.0中,委托被提出了,它使得传递函数成为可能,一句话就是委托就是强类型的函数指针,但委托比指针更强大。一般传递一个函数需要如下几步。
    1. 写一个委托(就像一个类)包含返回类型和参数类型
    2. 使用委托作为某一个函数的参数类型,这样,该函数就可以接受和委托描述的有着相同签名的函数了
    3. 将一个委托类型的函数传递给委托,创建一个委托实例。

    如果听起来很复杂,确实本来很复杂,但这是必需的。(虽然不是造火箭,但是比你认为的要更多的代码),然而步骤三不是必需的,编译器会为你做他,但是步骤1和2却是必不可少的。

    幸运的是C#2.0出现了泛型,现在我们也可以写泛型类,方法,更重要的是,泛型委托,然而,直到.net framework 3.5的时候。微软意识到实际上只有两种泛型委托(当然有一些不同的重载),会覆盖99%的使用情况:

    1.Action 没有任何输入参数,也没有输出参数。
    2.Action<t1,…t16> 需要1-16个参数,没有输出参数。
    3.Func<t1….t16,tout>需要0-16个参数,一个输出参数

    Action和其对应的泛型版本(仅仅是一个动作,执行一些事情)返回void的时候。Func则可以返回最后一个参数指定的类型,通过这两个委托类型,我们事实上,大部分情况下。前面提到的三步中的第一部就不用写的。而第二步仍然需要。

    那么如果我们想要运行代码的时候怎么做呢。在C#2.0中问题已经可以解决了。在这个版本里。我们可以创建委托方法,也就是一个匿名方法,然后这个语法一直未能流行起来,一个相当简化的匿名方法的版本类似这样:

    Func<double, double>  square = delegate (double x) {
    return x * x;
    }

    为了提高这种语法,欢迎来到Lambda表达式的国度。首先,这个Lambda名字怎么来的?事实上。来自于数学上的λ演算,更准确的说他是数学中 一个正式的系统。用于通过变量绑定和替换来进行表达式计算,所以我们有0-N个输入参数和一个返回值,而在编程中,也可以没有返回值

    我们看一下Lambda表达式的一些例子

    复制代码
    //编译器可以识别,然后就可以通过dummyLambda();来调用了
    var dummyLambda = () => { Console.WriteLine("Hallo World from a Lambda expression!"); };
    
    //可以通过类似 double y = square(25);来使用
    Func<double, double> square = x => x * x;
    
    //可以通过类似double z = product(9, 5);来使用
    Func<double, double,double> product = (x, y) => x * y;
    
    //可以通过类似printProduct(9, 5);来使用
    Action<double, double> printProduct = (x, y) => { Console.Writeline(x * y); };
    
    //可以通过类似var sum = dotProduct(new double[] { 1, 2, 3 }, new double[] { 4, 5, 6 });
    Func<double[], double[], double> dotProduct = (x, y) => {
    var dim = Math.Min(x.Length, y.Length);
    var sum = 0.0;
    for(var i = 0; i != dim; i++)
    sum += x[i] + y[i];
    return sum;
    };
    
    //可以通过类似 var result = matrixVectorProductAsync(...);使用
    Func<double[,], double[], double[]=""> matrixVectorProductAsync = async (x, y) => {
    var sum = 0.0;
    /* do some stuff ... */
    return sum;
    };
    复制代码

    从上面的代码段里我们可以学到一些东西

    • 如果我们只有一个输入参数,我们可以省略()
    • 如果我们有一个输入参数,并且向返回一个,那么我们可以省略{}和return关键字
    • 我们可以让我们的Lambda表达式异步执行,只要加上async关键字就可以了
    • Var关键字一般都不可以用,只有在极少的情况下有才可以。

    当然我们可以使用像平常那样使用var,如果我们指定了参数类型,这是可选的。因为类型可以从委托的类型中推断出来。而下面的情况都是错误的。请看下面的例子

    复制代码
    var square = (double x) => x * x;
    
    var stringLengthSquare = (string s) => s.Length * s.Length;
    
    var squareAndOutput = (decimal x, string s) => {
    var sqz = x * x;
    Console.WriteLine("Information by {0}: the square of {1} is {2}.", s, x, sqz);
    };
    复制代码

    大部分基本情况我们都知道。但是有些相当cool的东西(这使得他们在很多情况下很有用),我们考虑下面的代码片段

    var a = 5;
    var multiplyWith = x => x * a;
    var result1 = multiplyWith(10); //50
    a = 10;
    var result2 = multiplyWith(10); //100

    这是可以的。因此你可以使用其他的变量。这比你想象的要更特殊。因为这里出现了捕获变量。这使得出现了一个闭包,考虑下面的情况。

    复制代码
    void DoSomeStuff()
    {
    var coeff = 10;
    var compute = (int x) => coeff * x;
    var modifier = () => {
    coeff = 5;
    };
    
    var result1 = DoMoreStuff(compute);
    
    ModifyStuff(modifier);
    
    var result2 = DoMoreStuff(compute);
    }
    
    int DoMoreStuff(Action computer)
    {
    return computer(5);
    }
    
    void ModifyStuff(Action modifier)
    {
    modifier();
    }
     
    复制代码

    猜猜看这段代码会发生什么。首先我们创建了一个局部变量。和两个lambda表达式,第一额Lambda表达式展示在其他局部区域里访问局部变量是 可以的。这已经很让人难以置信了。这意味着我们想要保护一个变量,但事实上他仍然可以在其他方法里被访问。而不论方法是被定义在内部还是其他类的里。

    而第二个Lambda表达式则模拟了一个Lambda表达式可以修改外部域变量。这意味着我们可以在其他方法里修改我们的局部变量,仅仅需要传一个 在对应域里创建好的Lambda表达式就可以了。因此,我认为闭包有点魔幻的色彩了。就像并行编程一样。可能导致未期望的结果。就像在并行编程中的竞争情 况。

  • 相关阅读:
    【SPI】浅谈JDK中SPI技术
    【MySQL8.0.18】阿里云服务器上搭建MySQL数据库
    【JDK13】阿里云服务器安装JDK13
    小型个人博客搭建之数据库设计
    【算法题】CCF CSP第三题练习(更新中)
    【算法题】CCF CSP第一题练习
    【嵌入式】KEIL4和KEIL5合并
    【ROS】安装ubuntu18.04+ros-melodic
    Notepad++配置python之NppExec
    vi一般指令命令行
  • 原文地址:https://www.cnblogs.com/tonglei/p/4427792.html
Copyright © 2011-2022 走看看