1. 介绍
你可能不知道,但面试里最爱问的问题之一就是 “你对 lambda 表达式了解多少?” 为啥问这个?因为它在 C# 里超方便、很“时髦”,能写更少、更有表达力的代码。如果匿名方法是像手写信件,lambda 表达式就像发即时消息:快、简洁,有时候还能带个表情(差不多就是这样)。
lambda 表达式从 C# 3.0 开始引入,从那以后到处都是:在 LINQ、事件处理、并行、集合,甚至一些 .NET 的 AI 库里都用得到。
lambda 表达式 是直接在代码里“就地”声明方法的一种方式,没有名字,简短明了。基本语法通常是:
(参数) => 表达式_或_代码块
这个“箭头” => 叫做 “lambda 操作符” 或 “箭头”。
小小比喻
想象你写个食谱:“取个苹果,切一切,放到碗里”。
在 C# 中可能就是:
(苹果) => 切(苹果)
2. 把匿名方法改成 lambda
假设我们有一个委托:
delegate int SquareDelegate(int x);
匿名方法看起来会像这样:
SquareDelegate sq = delegate(int x) {
return x * x;
};
现在用 lambda 写一遍:
SquareDelegate sq = (int x) => { return x * x; };
但 C# 能推断类型,我们可以再简化:
SquareDelegate sq = x => x * x;
惊人的简洁!
和“常规”方法与匿名函数比较
| 方式 | 声明位置 | 可以在哪里使用 | 缺点 |
|---|---|---|---|
| 命名方法 | 在类里 | 任何地方 | 需要名字,代码较多 |
| 匿名方法 | 在代码中 | 只能与委托一起使用 | 语法更啰嗦 |
| lambda 表达式 | 在代码中 | 与委托一起的任何地方都能用 | 有时类型不明显 |
3. lambda 语法:几种变体
参数
无参数:
Action hello = () => Console.WriteLine("你好,世界!");
单参数(可以省略括号):
Func<int, int> inc = x => x + 1;
多个参数(需要括号):
Func<int, int, int> sum = (a, b) => a + b;
lambda 的主体
单表达式 — 不用大括号,也不用 return:
x => x * x
代码块 — 用大括号,需要 return:
(x, y) =>
{
int z = x + y;
return z * z;
}
参数类型
大多数情况下不需要显式写类型 — 编译器会推断。但如果你想写,也可以:
(x, y) => x + y // 编译器会推断类型.
(int x, int y) => x + y // 也可以显式写类型.
4. 细节和更实际的例子
与集合一起使用
记得我们的小练习(比如用户列表)吗?假设有个整型数组:
int[] numbers = { 1, 2, 3, 4, 5 };
需要筛选出偶数:
var evenNumbers = numbers.Where(n => n % 2 == 0);
这里 Where 是 LINQ 的扩展方法,条件就是我们的 lambda 过滤器。
作为参数传递给方法
假设我们有个委托方法:
public delegate bool Filter(int number);
public static int[] FilterNumbers(int[] data, Filter predicate)
{
var result = new List<int>();
foreach (var n in data)
if (predicate(n))
result.Add(n);
return result.ToArray();
}
现在传入一个 lambda:
int[] evens = FilterNumbers(numbers, n => n % 2 == 0);
把 lambda 当成事件处理器
button.Click += (sender, args) => Console.WriteLine("按钮被点击!");
5. lambda 与标准泛型委托
说到 lambda 就离不开 Func<>, Action<> 和 Predicate<>。它们是常用的委托类型:
- Action — 不返回值。
- Func — 返回值。
- Predicate — 返回 bool,通常用于筛选。
Action 示例:
Action<string> log = message => Console.WriteLine(message);
log("这是通过 lambda 的消息!");
Func 示例:
Func<int, int, int> multiply = (a, b) => a * b;
int product = multiply(3, 5); // 15
Predicate 示例:
Predicate<int> isNegative = n => n < 0;
bool test = isNegative(-7); // true
6. 多表达式的 lambda:什么时候需要代码块
如果 lambda 要做多步操作,用大括号并显式写 return:
Func<int, int, string> describeSum = (a, b) =>
{
int sum = a + b;
return $"总和: {sum}";
};
要是没写 return,编译器会抱怨(但不会讲太明白原因——就会说“并非所有路径都有返回值”)。
7. 返回 void:Action
当 lambda 不返回东西时,用 Action:
Action greet = () => Console.WriteLine("笑一笑 — 代码能编译通过!");
lambda 可以包含任意多条语句:
Action<int> printSquare = x =>
{
int sq = x * x;
Console.WriteLine($"数字 {x} 的平方是 {sq}");
};
8. 限制和常见错误
有时候你想随便写 lambda,但编译器不一定买账。
不能声明一个与外部已捕获变量同名的变量。
注意类型:如果委托签名和 lambda 不匹配,会报错。
如果委托要求返回值,就必须返回。
复杂的 lambda 别忘了大括号。单表达式 — 不用括号也不用 return。多条语句 — 用括号并显式 return。
GO TO FULL VERSION