什么是Lambda表达式
Lambda表达式其实就是一个用来代替委托实例的未命名的方法;
编译器会把Lambda表达式转化为以下二者之一:
- 一个委托实例
- 一个表达式树(expression tree),类型是Expression
,它表示了可遍历的对象模型中Lambda表达式里面的代码。它允许lambda表达式延迟到运行时再被解释。
- 实际上,编译器会通过编写一个私有方法来解析这个lambda表达式,然后把表达式的代码移动到这个方法里。
Lambda表达式的形式
- (parameters) => expression-or-statement-block
- (参数)=> 表达式或语句块
- 其中如果只有一个参数并且类型可推断的话,那么参数的小括号可以省略
lambda表达式与委托
- 每个lambda表达式的参数对应委托的参数
- 表达式的类型对应委托的返回类型
x => x * x;
delegate int Transformer(int i);
Lambda表达式 – 语句块
- Lambda表达式的代码也可以是语句块
x => {return x * X};
Func 和 Action
- Lambda表达式通常与Func和Action委托一起使用
显式指定Lambda表达式的参数类型
捕获外部变量
- lambda表达式可以引用本地的变量和所在方法的参数
被捕获的变量
- 被Lambda表达式引用的外部变量叫做被捕获的变量(captured variables)。
- 捕获了外部变量的lambda表达式叫做闭包。
- 被捕获的变量是在委托被实际调用的时候才被计算,而不是在捕获的时候。
- Lambda表达式本身也可以更新被捕获的变量
- 被捕获的变量的生命周期会被延长到和委托一样
捕获迭代变量
- 当捕获for循环的迭代变量时,C#会把这个变量当作是在循环外部定义的变量,这就意味着每次迭代捕获的都是同一个变量。
如何解决
注意:foreach
- C#4,和C#5+的区别
Lambda表达式 vs 本地方法
本地方法是C#7的一个新特性。它和Lambda表达式在功能上有很多重复之处,但它有*三个优点:
- 可以简单明了的进行递归
- 无需指定委托类型(那一堆代码)
- 性能开销略低一点
本地方法效率更高是因为它避免了委托的间接调用(需要CPU周期,内存分配)。本地方法也可以访问所在方法的本地变量,而且无需编译器把被捕获的变量hoist到隐藏的类。
匿名方法 vs Lambda表达式
匿名方法和Lambda表达式很像,但是缺少以下三个特性:
- 隐式类型参数
- 表达式语法(只能是语句块)
- 编译表达式树的能力,通过赋值给Expression
匿名方法 - 其它
- 捕获外部变量的规则和Lambda表达式是一样的。
- 但匿名方法可以完全省略参数声明,尽管委托需要参数
public event EventHandler Clicked = delegate{};
- 这就避免了触发事件前的null检查
//Notice that we omit the parameters:
Clicked += delegate {Console.Write("Clicked");};