首页 > 编程笔记 > C#笔记 阅读:28

C# Lamdba表达式用法详解(附带实例)

Lamdba 表达式是 C# 3.0 起开始支持的工具,其实这是委托的进一步应用,这是一种匿名的方法,使用上更具有弹性。

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 委托的说明和实例。

注意,Action 委托是 void 的回传类型。

① 0 个输入参数,基本类型 Action:
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

注意,上述程序中在执行 lambda 后 outerVariable 才加 1。

相关文章