C# Lamdba表达式用法详解(附带实例)
Lamdba 表达式是 C# 3.0 起开始支持的工具,其实这是委托的进一步应用,这是一种匿名的方法,使用上更具有弹性。
Lambda 表达式的核心是运算符“=>”,这个运算符又称 Lambda 运算符,由这个运算符区分输入参数和 Lambda 主体:
Lambda 有两种表达方式:
1) 表达式的 Lambda,概念如下:
2) 语句的Lambda,概念如下:
上述程序的 Lambda 表达式并没有明确指出回传值的数据类型,Lambda 表达式可以明确地设定回传值的数据类型,可以参考下列实例。
【实例 2】重新设计实例 1,明确指定 Lambda 表达式的数据类型。
【实例 4】使用委托搭配匿名方法设计实例 1。
【实例 5】使用委托搭配 Lambda 表达式重新设计实例 1。
如果将实例 4 和实例 5 做比较,可以发现实例 4 第 2 行的委托的匿名方法内容如下:
上述程序代码对实例 5 的 Lambda 表达式而言,相当于第 2 行的内容:
其实可以省略 Lambda 表达式,省略号的语句如下:
① 0 个输入参数,基本类型 Func<Tresult>:
② 1 个输入参数,基本类型 Func<T, Tresult>:
③ 两个输入参数,基本类型 Func<T1, T2, Tresult>:
【实例 6】0~2 个输入参数的测试。
其实 Lambda 表达式和 Func 委托组合,可以让整个程序设计简洁许多,例如,上述程序的第 11 行,如果使用一般语法设计则需要 4 行,如下:
注意,从 C# 9.0 开始可以使用下画线(_)舍弃没有使用的输入参数,舍弃参数对于事件处理程序会有帮助。
② 1 个输入参数,基本类型 Action<T>:
【实例 7】Lambda 转成 Action 委托的实例。
【实例 8】测试 delta 变量对 Lambda 表达式的影响。
【实例 9】Lambda表达式也可以时时更新捕获变量的值,读者可以参考本实例的输出。
Lambda 表达式的核心是运算符“=>”,这个运算符又称 Lambda 运算符,由这个运算符区分输入参数和 Lambda 主体:
输入参数 => Lambda主体
Lambda 有两种表达方式:
1) 表达式的 Lambda,概念如下:
(input - parameters) => expression例如:
x=> x+2
2) 语句的Lambda,概念如下:
(input - parameters) => { <sequence-of-statements> }例如:
x=> { Console.WriteLine(x+2); }注意,语句的 Lambda 与表达式的 Lambda 的最大差异在于,语句的 Lambda 可能会有 2~3 行主体内容。
C# Lambda基础语法
1) 没有输入参数的Lambda
如果 Lambda 没有输入参数,输入参数区可以使用空的小括号替代,这时语法如下:() => expression;
2) 有1个输入参数的Lambda
如果 Lambda 表达式只有 1 个输入参数,则可以省略小括号,这时语法如下:parameter => expression;
3) 有多个参数的Lambda
如果 Lambda 表达式有多个输入参数,假设有 3 个参数,这时语法如下:(p1, p2, p3) => expression;
C# Lambda基础实例
1) 表达式的 Lambda
【实例 1】用传统概念设计计算平方的 Lambda 表达式。var square = (int x) => x * x; Console.WriteLine(square(5));执行结果为:
25
上述程序中框起来的部分就是表达式的 Lambda,所以整个程序可以说是声明了一个变量 square。这个变量是匿名的,相当于 Lambda 表达式。(int x) => x * x;程序 var 声明的 square 不是一般值的变量,这是一个 var 声明的 Lambda 表达式变量,此变量有回传值,由第 3 行所输入的参数 5,代入 square() 后决定最后回传的值,所以 square() 其实是一个参考方法。同时所输入的参数必须是整数 int,回传的是该 x 值的平方。
上述程序的 Lambda 表达式并没有明确指出回传值的数据类型,Lambda 表达式可以明确地设定回传值的数据类型,可以参考下列实例。
【实例 2】重新设计实例 1,明确指定 Lambda 表达式的数据类型。
var square = (int x) => x * x; Console.WriteLine(square(5));执行结果同样是 25。
2) 语句的Lambda
【实例 3】将实例 1 改为语句体的 Lambda 重新设计。var square = (int x) => { return x * x; }; Console.WriteLine(square(5));执行结果同样是 25。
C# Lambda就是委托指定引用的匿名方法
Lambda 本质上就是结合委托的匿名方法实例。【实例 4】使用委托搭配匿名方法设计实例 1。
MySquare square = delegate(int x) { return x * x; }; Console.WriteLine(square(5)); delegate int MySquare(int i);执行结果与实例 1 相同。
【实例 5】使用委托搭配 Lambda 表达式重新设计实例 1。
MySquare square = int (int x) => x * x; Console.WriteLine(square(5)); delegate int MySquare(int i);执行结果同上。
如果将实例 4 和实例 5 做比较,可以发现实例 4 第 2 行的委托的匿名方法内容如下:
MySquare square = delegate (int x) { return x * x; };
上述程序代码对实例 5 的 Lambda 表达式而言,相当于第 2 行的内容:
MySquare square = int (int x) => x * x;
其实可以省略 Lambda 表达式,省略号的语句如下:
MySquare square = x => x * x;这就是说 Lambda 表达式就是委托指定引用的匿名方法的原因,当然另外要留意的是,第 2 行 Lambda 的参数与回传值需要和第 5 行声明委托 MySquare() 相同。
C#将Lambda表达式转换成delegate委托类型
从 C# 中的泛型委托 Func 和 Action,Lambda 可转换为这些委托类型。1) 将Lambda转为Func委托
下列是 0~2 个输入参数的 Lambda 转换成 Func 委托的说明和实例。① 0 个输入参数,基本类型 Func<Tresult>:
Func<string> myfun = () => "Hello! C#"; // 回传字符串上述 Func<string> 因为没有输入参数,所以角括号内的 string 代表是回传值类型。
② 1 个输入参数,基本类型 Func<T, Tresult>:
Func<int, int> myfun = x => x * x; // 回传平方在上述语句中因为 Func<int, int> 角括号的第 1 个 int 已经告知输入参数是整数,所以 Lambda 可以不必特别标记x变量是整数,另外角括号的第 2 个 int 是回传值数据类型。
③ 两个输入参数,基本类型 Func<T1, T2, Tresult>:
Func<int, int, bool> myfun = (x, y) => x == y; // 比较是否相同上述 Func<int, int, bool> 角括号内的前两个 int 代表输入数据是整数,回传值数据类型是布尔值。
【实例 6】0~2 个输入参数的测试。
// 0 个输入参数 Func<string> rtnString = () => "Hello! C#"; Console.WriteLine(rtnString()); // 1 个输入参数 Func<int, int> cube = x => x * x * x; Console.WriteLine(cube(5)); // 2 个输入参数 Func<int, int, bool> equal = (x, y) => x == y; Console.WriteLine(equal(5, 7));执行结果为:
Hello! C#
125
False
其实 Lambda 表达式和 Func 委托组合,可以让整个程序设计简洁许多,例如,上述程序的第 11 行,如果使用一般语法设计则需要 4 行,如下:
bool Equal(int x, int y) { return x == y; }但是现在使用 1 行就完成了过去要 4 行的程序代码。
注意,从 C# 9.0 开始可以使用下画线(_)舍弃没有使用的输入参数,舍弃参数对于事件处理程序会有帮助。
Func<int, int, int> constant = (_, _) => 50; // 回传常数值 50
2) 将Lambda转成Action委托
下列是 0~1 个输入参数的 Lambda 转换成 Action 委托的说明和实例。① 0 个输入参数,基本类型 Action:注意,Action 委托是 void 的回传类型。
Action myfun = () => Console.WriteLine("Hello! C#"); // 输出字符串
② 1 个输入参数,基本类型 Action<T>:
Action<int> myfun = x => Console.WriteLine(x * x); // 输出平方
【实例 7】Lambda 转成 Action 委托的实例。
// 0 个输入参数 Action noinput = () => Console.WriteLine("Action Lambda没有输入参数"); noinput(); // 1 个输入参数 Action<string> greet = name => { string greeting = $"北京 {name}!"; Console.WriteLine(greeting); }; greet("北大");执行结果为:
Action Lambda没有输入参数
北京 北大!
C#外在变量对Lambda表达式的影响
Lambda 表达式可以引用局部变量,这些变量称为外部变量(outer variable),被捕获后称为 闭包(closure)。它们在调用时计算,而非捕获时。【实例 8】测试 delta 变量对 Lambda 表达式的影响。
int delta = 10; Func<int, int> mul = (int x) => x * delta; delta = 5; var result = mul(5); Console.WriteLine(result);执行结果为:
25
上述第 2 行声明 Lambda 表达式时 delta 值是 10,但是第 3 行 delta 值改为 5,第 4 行实际调用 mul(5) 时,因为 delta 是 5,所以代入公式“x*delta”,可以得到公式“5*5”,最后结果是 25。【实例 9】Lambda表达式也可以时时更新捕获变量的值,读者可以参考本实例的输出。
int outerVariable = 1; Func<int> lambda = () => outerVariable++; Console.WriteLine(lambda()); Console.WriteLine(lambda()); Console.WriteLine(lambda()); Console.WriteLine(outerVariable);执行结果为:
1
2
3
4