首页 > 技术文章 > Lambda表达式

valiant1882331 2014-10-29 15:47 原文

  • Lambda表达式是写在委托实力上懂得匿名方法.编译器立即将lambda表达式转换成下面这两种形式
  1. 委托实例
  2. Expression<Tdelegate>类型的表达式树,该表达式树将lambda表达式内的代码显示为可遍历的对象模型,是表达式的解释可以延迟到运行时.
  • 下面的委托类型:

  delegate int Transformer(int i);

  可以指定和调用下面的x=>x*x lambda表达式:

  编译器内部将这种lambda表达式编译成一个私有方法,并把表达式代码移到该方法中.

Transformer sqr=x=>x*x;
Console,WriteLine(sqr(3)); //9
View Code
  • Lambda表达式有一下形式:

  (参数)=>表达式或语句块

  在只有一个可推测类型的参数的时候,可以省略小括号.

  上面例子中,只有一个参数x,表达式为x*x:

  x=>x*x;

  • Lambda表达式使每个参数和委托的参数一致,表达式的类型(可以为void)和委托的返回值类型一致

  上面例子中,x和参数i一致,表达式x*x和返回值类型int一致,因此与Transformer委托相兼容

  • Lambda表达式代码可以是表达式,还可以是语句块,可以把以上的改成
    x=>{return x*x;};
    View Code

     

  • Lambda表达式通常和Func或Action委托一起使用,因此,修改以前的代码为
    Func<int,int>sqr=x=>x*x;
    View Code

    带两个参数的表达式

    Func<string,string,int>totalLength=(s1,s2)=>s1.Length+s2.Length;
    int total=totalLength("Hello","world");//total=10;
    View Code

     

  • 明确指定Lambda表达式参数类型.
    编译器通常可以根据上下文推断出lambda参数的类型,但是,当不能推断时,必须明确指定每个参数的类型
    Func<int,int>sqr=x=>x*x;
    View Code

    编译器可以推断出x是int类型,也可以显示的指定x的类型:

    Func<int,int>=(int x)=>x*x;
    View Code

     

  • Lambda表达式可以引用方法内的内部变量和参数(外部变量).
    static void Main(string[] args)
    {
          int factor = 2;
          Func<int, int> multiplier = n => n * factor;
          Console.WriteLine(multiplier(3));
          Console.ReadKey();
    }
    View Code

    结果为6.

  • Lambda表达式引用的外部变量称为捕获变量,捕获变量的表达式称为一个闭包.(捕获变量在真正调用委托时被赋值,而不是在捕获时赋值).
    static void Main(string[] args)
    {
           int factor = 2;
           Func<int, int> multiplier = n => n * factor;
           Console.WriteLine(multiplier(3));//6
           factor = 10;
           Console.WriteLine(multiplier(3));//30
           Console.ReadKey();
    }
    View Code

     Lambda表达式可以自动更新捕获变量;

    int seed = 2;
    Func<int> natural = () => seed++;
    Console.WriteLine(natural());//0
    Console.WriteLine(natural());//1
    Console.WriteLine(natural());//2
    View Code

    捕获变量的生命周期可以和委托的生命周期相同.以下例子中,局部变量seed本应在Natural执行完后消失,但是因为被捕获了,则,它的生命周期延长到和捕获它的委托natural的生命周期相同:

    static void Main(string[] args)
    {
         Func<int> natural = Natural();
         Console.WriteLine(natural());//0
         Console.WriteLine(natural());//1
    }
    static Func<int>Natural()
    {
          int seed = 0;
          return () => seed++;//返回一个闭包
    }
    View Code

     

  • 在Lambda表达式内实例化的局部变量,在每次调用委托实例期间是唯一的,如果我们把上例改成在lambda表达式内部实例化seed,程序结果会与之前不同:
    static void Main(string[] args)
    {
        Func<int> natural = Natural();
        Console.WriteLine(natural());//0
        Console.WriteLine(natural());//0
    }
    static Func<int>Natural()
    {
         return () => { int seed = 0; return seed++; };//返回一个闭包
    }
    View Code

     

  • 捕获循环变量
    当捕获for或foreach语句中的循环变量时,c#把这些循环变量看作是声明在循环外部的,这表名每个循环捕获的是相同的变量,
    Action[] actions = new Action[3];
    for (int i = 0; i < 3; i++)
    {
        actions[i] = () => Console.Write(i);
    }
    foreach (var item in actions)
    {
         item();//结果输出333,而不是012
    }
    Console.ReadKey();
    View Code

    每个闭包(Console.Write(i))捕获相同的变量,(如果变量i在循环中保持不变,则非常有用;甚至可以根据需要在循环体中显示修改i的值.),每个委托在调用的时候才看到i值,此时,i值为3(循环完成的最后值)

    Action[] actions = new Action[3];
    int i = 0;
    actions[0] = () => Console.Write(i);
    i = 1;
    actions[1] = () => Console.Write(i);
    i = 2;
    actions[2] = () => Console.Write(i);
    i = 3;
    //item调用的时候捕获最后一次i的值
    foreach (var item in actions) item();//333
    View Code

    想要输出012,

    Action[] actions = new Action[3];
    for (int i = 0; i < 3; i++)
    {
        int lo = i;
       actions[i] = () => Console.Write(lo);
    }
    foreach (var item in actions)item();//012
    View Code

    这样导致闭包每次循环中捕获不同的变量

推荐阅读