Lambda表达式
- Lambda表达式是写在委托实力上懂得匿名方法.编译器立即将lambda表达式转换成下面这两种形式
- 委托实例
- Expression<Tdelegate>类型的表达式树,该表达式树将lambda表达式内的代码显示为可遍历的对象模型,是表达式的解释可以延迟到运行时.
- 下面的委托类型:
delegate int Transformer(int i);
可以指定和调用下面的x=>x*x lambda表达式:
编译器内部将这种lambda表达式编译成一个私有方法,并把表达式代码移到该方法中.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Transformer sqr=x=>x*x; Console,WriteLine(sqr(3)); //9
- Lambda表达式有一下形式:
(参数)=>表达式或语句块
在只有一个可推测类型的参数的时候,可以省略小括号.
上面例子中,只有一个参数x,表达式为x*x:
x=>x*x;
- Lambda表达式使每个参数和委托的参数一致,表达式的类型(可以为void)和委托的返回值类型一致
上面例子中,x和参数i一致,表达式x*x和返回值类型int一致,因此与Transformer委托相兼容
- Lambda表达式代码可以是表达式,还可以是语句块,可以把以上的改成
x=>{return x*x;};
- Lambda表达式通常和Func或Action委托一起使用,因此,修改以前的代码为
Func<int,int>sqr=x=>x*x;
带两个参数的表达式
Func<string,string,int>totalLength=(s1,s2)=>s1.Length+s2.Length; int total=totalLength("Hello","world");//total=10;
- 明确指定Lambda表达式参数类型.
编译器通常可以根据上下文推断出lambda参数的类型,但是,当不能推断时,必须明确指定每个参数的类型Func<int,int>sqr=x=>x*x;
编译器可以推断出x是int类型,也可以显示的指定x的类型:
Func<int,int>=(int x)=>x*x;
- Lambda表达式可以引用方法内的内部变量和参数(外部变量).
static void Main(string[] args) { int factor = 2; Func<int, int> multiplier = n => n * factor; Console.WriteLine(multiplier(3)); Console.ReadKey(); }
结果为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(); }
Lambda表达式可以自动更新捕获变量;
int seed = 2; Func<int> natural = () => seed++; Console.WriteLine(natural());//0 Console.WriteLine(natural());//1 Console.WriteLine(natural());//2
捕获变量的生命周期可以和委托的生命周期相同.以下例子中,局部变量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++;//返回一个闭包 }
- 在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++; };//返回一个闭包 }
- 捕获循环变量
当捕获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();
每个闭包(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
想要输出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
这样导致闭包每次循环中捕获不同的变量